├── event-email-service ├── .gitignore ├── .eslinterrc ├── .dockerignore ├── Dockerfile ├── package.json ├── src │ ├── tracing.js │ └── app.js ├── Makefile ├── tests │ └── tests.js ├── deployment │ └── deployment.yaml └── README.md ├── orders-service ├── .gitignore ├── .dockerignore ├── go.mod ├── internal │ ├── service │ │ ├── model │ │ │ └── order_type.go │ │ └── order_service.go │ ├── store │ │ ├── store.go │ │ ├── redis_store.go │ │ └── memory_store.go │ └── handler │ │ ├── webhook_handler.go │ │ └── order_handler.go ├── chart │ ├── Chart.yaml │ ├── .helmignore │ ├── templates │ │ ├── service.yaml │ │ ├── _helpers.tpl │ │ └── deployment.yaml │ └── values.yaml ├── deployment │ ├── orders-service-service.yaml │ ├── orders-service-deployment.yaml │ └── orders-function.yaml ├── Makefile ├── Dockerfile ├── cmd │ └── main.go ├── docs │ └── openapi.yaml └── README.md ├── prometheus ├── monitoring-custom-metrics │ ├── .gitignore │ ├── Gopkg.toml │ ├── custom.Dockerfile │ ├── Makefile │ ├── deployment │ │ ├── service-monitor.yaml │ │ └── deployment.yaml │ ├── go.mod │ ├── README.md │ ├── Gopkg.lock │ └── main.go ├── README.md ├── istio │ ├── servicemonitor-istiod.yaml │ └── podmonitor-istio-proxy.yaml ├── monitoring-alert-rules │ ├── deployment │ │ └── alert-rule.yaml │ └── README.md ├── alertmanager-values.yaml ├── README-old.md ├── monitoring-grafana-dashboard │ └── README.md ├── assets │ └── monitoring-tutorials.svg └── prometheus.md ├── NOTICE.md ├── http-db-service ├── .gitignore ├── internal │ ├── mssqldb │ │ ├── connection_test.go │ │ ├── connection.go │ │ ├── queries.go │ │ ├── queries_test.go │ │ ├── factory_test.go │ │ ├── config.go │ │ └── factory.go │ └── repository │ │ ├── factory.go │ │ ├── api.go │ │ ├── memory.go │ │ ├── mock_dbQuerier.go │ │ ├── mock_OrderRepository.go │ │ ├── db_test.go │ │ ├── repo_integration_test.go │ │ ├── memory_test.go │ │ └── db.go ├── handler │ ├── response │ │ └── response.go │ ├── api.go │ ├── events │ │ ├── order_created.go │ │ └── order_created_test.go │ ├── api_test.go │ └── order.go ├── Dockerfile ├── Gopkg.toml ├── Makefile ├── config │ └── service.go ├── deployment │ └── deployment.yaml ├── main.go ├── README.md ├── build.sh ├── docs │ └── api │ │ └── api.yaml └── Gopkg.lock ├── jaeger └── README.md ├── loki └── README.md ├── SECURITY.md ├── trace-demo └── README.md ├── .github ├── issue-template.md ├── workflows │ └── markdown-link-check.yml ├── ISSUE_TEMPLATE │ ├── feature-request.md │ ├── documentation-improvement.md │ ├── bug-report.md │ └── security-vulnerability.md ├── pull-request-template.md └── stale.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── kiali ├── apirule.yaml ├── values.yaml └── README.md ├── service-binding ├── lambda │ ├── deployment │ │ ├── redis-instance.yaml │ │ └── lambda-function.yaml │ └── README.md └── service │ ├── deployment │ ├── mssql-instance.yaml │ └── http-db-service.yaml │ └── README.md ├── gateway └── service │ ├── api-without-auth.yaml │ ├── api-without-auth-allow.yaml │ ├── oauth2client.yaml │ ├── api-with-oauth2.yaml │ ├── api-with-jwt.yaml │ └── deployment.yaml ├── CODEOWNERS ├── .reuse └── dep5 ├── README.md ├── CONTRIBUTING.md └── LICENSES └── Apache-2.0.txt /event-email-service/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .eslintrc* 3 | -------------------------------------------------------------------------------- /event-email-service/.eslinterrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app" 3 | } -------------------------------------------------------------------------------- /orders-service/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | bin/ 4 | vendor/ 5 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | vendor/ 3 | -------------------------------------------------------------------------------- /event-email-service/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .eslintrc* 3 | *.md 4 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. 2 | -------------------------------------------------------------------------------- /orders-service/.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore all 2 | ** 3 | # Allow 4 | !internal 5 | !pkg 6 | !cmd 7 | !go.mod 8 | !go.sum 9 | -------------------------------------------------------------------------------- /http-db-service/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | .vscode/ 4 | /debug 5 | /temp_* 6 | vendor/ 7 | .DS_Store 8 | telepresence.log -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | 3 | required = [ 4 | "golang.org/x/lint/golint" 5 | ] 6 | 7 | [[constraint]] 8 | version = "v0.9.2" 9 | name = "github.com/prometheus/client_golang" 10 | -------------------------------------------------------------------------------- /jaeger/README.md: -------------------------------------------------------------------------------- 1 | # Install custom Jaeger in Kyma 2 | 3 | The document has been moved. Find the updated instructions in [Integrate With Jaeger](https://kyma-project.io/#/telemetry-manager/user/integration/jaeger/README). 4 | -------------------------------------------------------------------------------- /loki/README.md: -------------------------------------------------------------------------------- 1 | # Installing a custom Loki stack in Kyma 2 | 3 | The document has been moved. Find the updated instructions in [Integrate With Loki](https://kyma-project.io/#/telemetry-manager/user/integration/loki/README). 4 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you suspect you have found a security vulnerability in Kyma, please report it to us as described in the [SAP Open Source Security Policy](https://github.com/SAP/.github/blob/main/SECURITY.md). 4 | -------------------------------------------------------------------------------- /orders-service/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kyma-project/examples/orders-service 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/go-redis/redis/v8 v8.0.0-beta.7 7 | github.com/gorilla/mux v1.7.4 8 | github.com/rs/cors v1.7.0 9 | ) 10 | -------------------------------------------------------------------------------- /event-email-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | LABEL source=git@github.com:kyma-project/examples.git 4 | 5 | WORKDIR /usr/src/app 6 | 7 | COPY . . 8 | 9 | RUN npm install 10 | 11 | EXPOSE 3000 12 | 13 | CMD [ "npm", "start" ] 14 | -------------------------------------------------------------------------------- /orders-service/internal/service/model/order_type.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Order struct { 4 | Code string `json:"orderCode"` 5 | ConsignmentCode string `json:"consignmentCode"` 6 | ConsignmentStatus string `json:"consignmentStatus"` 7 | } 8 | -------------------------------------------------------------------------------- /orders-service/chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: Kyma example 'orders-service' 3 | name: kyma-example-orders-service 4 | version: 0.1.0 5 | home: https://kyma-project.io 6 | icon: https://github.com/kyma-project/kyma/blob/main/logo.png?raw=true 7 | -------------------------------------------------------------------------------- /trace-demo/README.md: -------------------------------------------------------------------------------- 1 | # Install OpenTelemetry Demo Application in Kyma 2 | 3 | The document has been moved. Find the updated instructions in [Integrate OpenTelemetry Demo App](https://kyma-project.io/#/telemetry-manager/user/integration/opentelemetry-demo/README). 4 | -------------------------------------------------------------------------------- /.github/issue-template.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | **Description** 7 | 8 | 9 | -------------------------------------------------------------------------------- /prometheus/README.md: -------------------------------------------------------------------------------- 1 | # Monitoring in Kyma using a custom kube-prometheus-stack 2 | 3 | > [!WARNING] 4 | > This guide has been revised and moved. Find the updated instructions in [Integrate With Prometheus](https://kyma-project.io/#/telemetry-manager/user/integration/prometheus/README). 5 | > The archived version is still available [here](./README-old.md) 6 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/custom.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.21.0 AS builder 2 | RUN mkdir /app 3 | ADD . /app 4 | WORKDIR /app 5 | RUN CGO_ENABLED=0 GOOS=linux go build -o main ./... 6 | 7 | FROM scratch 8 | LABEL source=git@github.com:kyma-project/examples.git 9 | COPY --from=builder /app . 10 | EXPOSE 8080 11 | 12 | CMD ["./main"] 13 | -------------------------------------------------------------------------------- /prometheus/istio/servicemonitor-istiod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | name: istiod 5 | spec: 6 | selector: 7 | matchExpressions: 8 | - {key: istio, operator: In, values: [pilot]} 9 | namespaceSelector: 10 | matchNames: 11 | - istio-system 12 | endpoints: 13 | - port: http-monitoring 14 | -------------------------------------------------------------------------------- /prometheus/istio/podmonitor-istio-proxy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: PodMonitor 3 | metadata: 4 | name: istio-proxy 5 | spec: 6 | selector: 7 | matchLabels: 8 | security.istio.io/tlsMode: 'istio' 9 | namespaceSelector: 10 | any: true 11 | podMetricsEndpoints: 12 | - port: http-envoy-prom 13 | path: /stats/prometheus 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */debug 2 | */.vscode-janus-debug 3 | */jsconfig.json 4 | */order-service.exe 5 | */example-http-db-service.exe 6 | */vscode/launch.json 7 | */.idea/ 8 | .idea/ 9 | # Binaries 10 | */*.exe 11 | */*.dll 12 | */*.so 13 | */*.dylib 14 | */main 15 | .DS_Store 16 | monitoring-custom-metrics/bin 17 | 18 | # Vendor 19 | monitoring-custom-metrics/vendor 20 | tests/http-db-service/vendor 21 | -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/connection_test.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestConnectionString(t *testing.T) { 10 | // when 11 | cURL := newSQLServerConnectionURL("u", "p", "host", "mydb", 5432) 12 | //then 13 | assert.Equal(t, cURL.String(), "sqlserver://u:p@host:5432?database=mydb") 14 | } 15 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | Each contributor and maintainer of this project agrees to follow the [community Code of Conduct](https://github.com/kyma-project/community/blob/main/CODE_OF_CONDUCT.md) that relies on the CNCF Code of Conduct. Read it to learn about the agreed standards of behavior, shared values that govern our community, and details on how to report any suspected Code of Conduct violations. 4 | -------------------------------------------------------------------------------- /kiali/apirule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.kyma-project.io/v1beta1 2 | kind: APIRule 3 | metadata: 4 | name: kiali 5 | spec: 6 | host: kiali-server 7 | service: 8 | name: kiali-server 9 | port: 20001 10 | gateway: kyma-system/kyma-gateway 11 | rules: 12 | - path: /.* 13 | methods: ["GET", "POST"] 14 | accessStrategies: 15 | - handler: noop 16 | mutators: 17 | - handler: noop 18 | -------------------------------------------------------------------------------- /orders-service/deployment/orders-service-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: orders-service 5 | namespace: orders-service 6 | labels: 7 | app: orders-service 8 | example: orders-service 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - name: http 13 | port: 80 14 | protocol: TCP 15 | targetPort: 8080 16 | selector: 17 | app: orders-service 18 | example: orders-service 19 | -------------------------------------------------------------------------------- /service-binding/lambda/deployment/redis-instance.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: servicecatalog.k8s.io/v1beta1 3 | kind: ServiceInstance 4 | metadata: 5 | name: redis-instance 6 | labels: 7 | example: service-binding-lambda 8 | spec: 9 | clusterServiceClassExternalName: redis 10 | clusterServicePlanExternalName: micro 11 | parameters: 12 | redisPassword: SWxwQmNVVjJlbU5QUVc4aQo= 13 | resources: 14 | requests: 15 | memory: 96Mi 16 | -------------------------------------------------------------------------------- /orders-service/chart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | -------------------------------------------------------------------------------- /event-email-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ec-email-service", 3 | "version": "0.0.1", 4 | "description": "Sends automated emails when an event is received.", 5 | "main": "src/app.js", 6 | "scripts": { 7 | "start": "node src/app.js", 8 | "test": "mocha tests/*.js" 9 | }, 10 | "dependencies": { 11 | "express": "^4.18.3" 12 | }, 13 | "devDependencies": { 14 | "chai": "^4.4.1", 15 | "chai-http": "^4.4.0", 16 | "mocha": "^10.3.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/markdown-link-check.yml: -------------------------------------------------------------------------------- 1 | name: Markdown Links Check 2 | run-name: ${{github.event.pull_request.title}} 3 | on: [ pull_request ] 4 | jobs: 5 | markdown-link-check: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 10 | with: 11 | use-verbose-mode: 'no' 12 | config-file: '.mlc.config.json' 13 | folder-path: '.' 14 | max-depth: -1 15 | -------------------------------------------------------------------------------- /service-binding/service/deployment/mssql-instance.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: servicecatalog.k8s.io/v1beta1 2 | kind: ServiceInstance 3 | metadata: 4 | name: mssql-instance 5 | labels: 6 | example: service-binding-service 7 | spec: 8 | clusterServiceClassExternalName: azure-sqldb 9 | clusterServicePlanExternalName: basic 10 | parameters: 11 | location: eastus 12 | resourceGroup: examples 13 | firewallStartIPAddress: "0.0.0.0" 14 | firewallEndIPAddress: "255.255.255.255" 15 | -------------------------------------------------------------------------------- /gateway/service/api-without-auth.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: gateway.kyma-project.io/v1alpha1 3 | kind: APIRule 4 | metadata: 5 | labels: 6 | example: gateway-service 7 | name: http-db-service 8 | spec: 9 | service: 10 | host: http-db-service 11 | name: http-db-service 12 | port: 8017 13 | gateway: kyma-gateway.kyma-system.svc.cluster.local 14 | rules: 15 | - path: /.* 16 | methods: ["GET"] 17 | accessStrategies: 18 | - handler: noop 19 | mutators: [] -------------------------------------------------------------------------------- /gateway/service/api-without-auth-allow.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: gateway.kyma-project.io/v1alpha1 3 | kind: APIRule 4 | metadata: 5 | labels: 6 | example: gateway-service 7 | name: http-db-service 8 | spec: 9 | service: 10 | host: http-db-service 11 | name: http-db-service 12 | port: 8017 13 | gateway: kyma-gateway.kyma-system.svc.cluster.local 14 | rules: 15 | - path: /.* 16 | methods: ["GET"] 17 | accessStrategies: 18 | - handler: allow 19 | mutators: [] -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/connection.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | ) 7 | 8 | func newSQLServerConnectionURL(user, pass, host, dbName string, port int) *url.URL { 9 | query := url.Values{} 10 | query.Add("database", dbName) 11 | 12 | connURL := &url.URL{ 13 | Scheme: "sqlserver", 14 | User: url.UserPassword(user, pass), 15 | Host: fmt.Sprintf("%s:%d", host, port), 16 | RawQuery: query.Encode(), 17 | } 18 | 19 | return connURL 20 | } 21 | -------------------------------------------------------------------------------- /gateway/service/oauth2client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | labels: 5 | example: gateway-service 6 | name: http-db-service 7 | data: 8 | client_id: ZXhhbXBsZS1pZA== 9 | client_secret: ZXhhbXBsZS1zZWNyZXQ= 10 | --- 11 | apiVersion: hydra.ory.sh/v1alpha1 12 | kind: OAuth2Client 13 | metadata: 14 | labels: 15 | example: gateway-service 16 | name: http-db-service 17 | spec: 18 | grantTypes: 19 | - "client_credentials" 20 | scope: "read write" 21 | secretName: http-db-service -------------------------------------------------------------------------------- /event-email-service/src/tracing.js: -------------------------------------------------------------------------------- 1 | const traceHeaders = [ 2 | 'Traceparent', 3 | 'Tracestate', 4 | 'Baggage', 5 | 'X-Request-Id', 6 | 'X-B3-Traceid', 7 | 'X-B3-Spanid', 8 | 'X-B3-Parentspanid', 9 | 'X-B3-Sampled', 10 | 'X-B3-Flags', 11 | 'X-Ot-Span-Context' 12 | ]; 13 | 14 | module.exports.propagateTracingHeaders = function(headers, downstreamReq) { 15 | for (var h in traceHeaders) { 16 | let headerVal = headers[h]; 17 | if (headerVal !== undefined) { 18 | downstreamReq.headers[h] = headerVal; 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /gateway/service/api-with-oauth2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.kyma-project.io/v1alpha1 2 | kind: APIRule 3 | metadata: 4 | labels: 5 | example: gateway-service 6 | name: http-db-service 7 | spec: 8 | gateway: kyma-gateway.kyma-system.svc.cluster.local 9 | service: 10 | host: http-db-service 11 | name: http-db-service 12 | port: 8017 13 | rules: 14 | - path: /.* 15 | methods: ["GET"] 16 | mutators: [] 17 | accessStrategies: 18 | - handler: oauth2_introspection 19 | config: 20 | required_scope: ["read", "write"] -------------------------------------------------------------------------------- /orders-service/internal/store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | type Store interface { 9 | Add(ctx context.Context, key, value string) error 10 | Get(ctx context.Context, key string) (string, error) 11 | Keys(ctx context.Context, pattern string) ([]string, error) 12 | Delete(ctx context.Context, key string) error 13 | Clear(ctx context.Context) error 14 | GetStoreType(ctx context.Context) string 15 | } 16 | 17 | var ( 18 | NotFoundError = errors.New("object not found") 19 | AlreadyExistsError = errors.New("object already exists") 20 | ) 21 | -------------------------------------------------------------------------------- /http-db-service/handler/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "encoding/json" 5 | log "github.com/Sirupsen/logrus" 6 | "net/http" 7 | ) 8 | 9 | type Body struct { 10 | Status int `json:"status"` 11 | Message string `json:"message"` 12 | } 13 | 14 | func WriteCodeAndMessage(code int, msg string, w http.ResponseWriter) { 15 | w.Header().Set("Content-Type", "application/json;charset=UTF-8") 16 | w.WriteHeader(code) 17 | response := Body{code, msg} 18 | if err := json.NewEncoder(w).Encode(response); err != nil { 19 | log.Error("Error sending response", err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /orders-service/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME = orders-service 2 | IMG = $(DOCKER_PUSH_REPOSITORY)$(DOCKER_PUSH_DIRECTORY)/$(APP_NAME) 3 | TAG = $(DOCKER_TAG) 4 | 5 | .PHONY: docker-compose 6 | test: 7 | docker-compose -f ./docker-compose.yaml 8 | 9 | .PHONY: test 10 | test: 11 | go test ./... -count=1 12 | 13 | .PHONY: build-image 14 | build-image: 15 | docker build -t $(APP_NAME):latest . 16 | 17 | .PHONY: push-image 18 | push-image: 19 | docker tag $(APP_NAME) $(IMG):$(TAG) 20 | docker push $(IMG):$(TAG) 21 | 22 | .PHONY: ci-pr 23 | ci-pr: test build-image push-image 24 | 25 | .PHONY: ci-main 26 | ci-main: test build-image push-image 27 | -------------------------------------------------------------------------------- /http-db-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.12 as builder 2 | ARG DOCK_PKG_DIR=/go/src/github.com/kyma-project/examples/http-db-service/ 3 | ADD . $DOCK_PKG_DIR 4 | WORKDIR $DOCK_PKG_DIR 5 | RUN go get -t -d -v -insecure ./... 6 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . 7 | RUN go test ./... 8 | 9 | 10 | FROM scratch 11 | LABEL source=git@github.com:kyma-project/examples.git 12 | WORKDIR /app/ 13 | COPY --from=builder /go/src/github.com/kyma-project/examples/http-db-service/main /app/ 14 | COPY --from=builder /go/src/github.com/kyma-project/examples/http-db-service/docs/api/api.yaml /app/ 15 | CMD ["./main"] 16 | 17 | EXPOSE 8017:8017 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an improvement to the project 4 | --- 5 | 6 | 10 | 11 | **Description** 12 | 13 | 14 | 15 | **Reasons** 16 | 17 | 18 | 19 | **Attachments** 20 | 21 | 22 | -------------------------------------------------------------------------------- /prometheus/monitoring-alert-rules/deployment/alert-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: PrometheusRule 3 | metadata: 4 | name: pod-not-running-rule 5 | namespace: kyma-system 6 | labels: 7 | app: monitoring 8 | release: monitoring 9 | spec: 10 | groups: 11 | - name: pod-not-running-rule 12 | rules: 13 | - alert: PodNotRunning 14 | expr: absent(kube_pod_container_status_running{pod=~"^http-db-service(.*)"}) 15 | for: 10s 16 | labels: 17 | severity: critical 18 | annotations: 19 | description: "http-db-service Pod is not running" 20 | summary: "http-db-service Pod is not running" 21 | -------------------------------------------------------------------------------- /orders-service/chart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "fullname" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | app: {{ template "fullname" . }} 8 | example: {{ template "fullname" . }} 9 | release: {{ .Release.Name }} 10 | spec: 11 | type: {{ .Values.service.type }} 12 | ports: 13 | - name: {{ .Values.service.name }} 14 | port: {{ .Values.service.port }} 15 | protocol: {{ .Values.service.protocol }} 16 | targetPort: {{ .Values.service.targetPort }} 17 | selector: 18 | app: {{ template "fullname" . }} 19 | example: {{ template "fullname" . }} 20 | release: {{ .Release.Name }} 21 | -------------------------------------------------------------------------------- /http-db-service/Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | [[constraint]] 3 | branch = "master" 4 | name = "github.com/denisenkom/go-mssqldb" 5 | 6 | [[constraint]] 7 | name = "github.com/gorilla/mux" 8 | version = "1.7.1" 9 | 10 | [[constraint]] 11 | name = "github.com/lib/pq" 12 | version = "1.1.0" 13 | 14 | [[constraint]] 15 | name = "github.com/pkg/errors" 16 | version = "0.8.1" 17 | 18 | [[constraint]] 19 | name = "github.com/rs/cors" 20 | version = "1.3.0" 21 | 22 | [[constraint]] 23 | name = "github.com/stretchr/testify" 24 | version = "1.3.0" 25 | 26 | [[constraint]] 27 | name = "github.com/vrischmann/envconfig" 28 | version = "1.1.0" 29 | 30 | [prune] 31 | go-tests = true 32 | unused-packages = true 33 | -------------------------------------------------------------------------------- /orders-service/chart/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for 'orders-service'. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | fullnameOverride: "orders-service" 6 | 7 | image: 8 | repository: "eu.gcr.io/kyma-project/develop/orders-service" 9 | tag: "e8175c63" 10 | pullPolicy: IfNotPresent 11 | 12 | deployment: 13 | replicas: 1 14 | 15 | pod: 16 | resources: 17 | limits: 18 | cpu: 20m 19 | memory: 32Mi 20 | requests: 21 | cpu: 10m 22 | memory: 16Mi 23 | env: 24 | appPort: "8080" 25 | redisPrefix: "REDIS_" 26 | 27 | service: 28 | type: ClusterIP 29 | name: http 30 | port: 80 31 | protocol: TCP 32 | targerPort: 8080 33 | -------------------------------------------------------------------------------- /gateway/service/api-with-jwt.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.kyma-project.io/v1alpha1 2 | kind: APIRule 3 | metadata: 4 | labels: 5 | example: gateway-service 6 | name: http-db-service 7 | spec: 8 | gateway: kyma-gateway.kyma-system.svc.cluster.local 9 | rules: 10 | - accessStrategies: 11 | - config: 12 | jwks_urls: 13 | - http://dex-service.kyma-system.svc.cluster.local:5556/keys 14 | trusted_issuers: 15 | - https://dex.kyma.local 16 | handler: jwt 17 | methods: 18 | - GET 19 | - POST 20 | - PUT 21 | - DELETE 22 | path: /.* 23 | service: 24 | host: http-db-service 25 | name: http-db-service 26 | port: 8017 -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME = monitoring-custom-metrics 2 | IMG = $(DOCKER_PUSH_REPOSITORY)$(DOCKER_PUSH_DIRECTORY)/$(APP_NAME) 3 | TAG = $(DOCKER_TAG) 4 | 5 | resolve: 6 | dep ensure -vendor-only -v 7 | 8 | build: 9 | go mod vendor 10 | go generate ./... 11 | CGO_ENABLED=0 go build -o ./bin/app $(buildpath) 12 | 13 | .PHONY: build-image 14 | build-image: 15 | docker build -t $(APP_NAME):latest . 16 | 17 | .PHONY: push-image 18 | push-image: 19 | docker tag $(APP_NAME) $(IMG):$(TAG) 20 | docker push $(IMG):$(TAG) 21 | 22 | .PHONY: ci-pr 23 | ci-pr: resolve build build-image push-image 24 | 25 | .PHONY: ci-main 26 | ci-main: resolve build build-image push-image 27 | 28 | clean: 29 | rm -f ./bin/app 30 | -------------------------------------------------------------------------------- /.github/pull-request-template.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | **Description** 9 | 10 | Changes proposed in this pull request: 11 | 12 | - ... 13 | - ... 14 | - ... 15 | 16 | **Related issue(s)** 17 | 18 | -------------------------------------------------------------------------------- /http-db-service/handler/api.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | // SwaggerAPIHandler provides the Swagger API specification file 9 | func SwaggerAPIHandler(w http.ResponseWriter, r *http.Request) { 10 | w.Header().Set("Content-Type", "text/yaml;charset=UTF-8") 11 | http.ServeFile(w, r, "api.yaml") 12 | } 13 | 14 | // SwaggerAPIRedirectHandler is used to redirect from root (of the service) to the file which contains the service's API 15 | func SwaggerAPIRedirectHandler(w http.ResponseWriter, r *http.Request) { 16 | protocol := "http" 17 | if r.TLS != nil { 18 | protocol = "https" 19 | } 20 | 21 | rd := fmt.Sprintf("%s://editor.swagger.io/#/?url=%s://%s/api.yaml", protocol, protocol, r.Host) 22 | http.Redirect(w, r, rd, http.StatusPermanentRedirect) 23 | } 24 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/deployment/service-monitor.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: metrics 6 | labels: 7 | app: sample-metrics 8 | example: monitoring-custom-metrics 9 | spec: 10 | selector: 11 | matchLabels: 12 | app: sample-metrics 13 | endpoints: 14 | - port: http 15 | scheme: https 16 | tlsConfig: 17 | caFile: /etc/prometheus/secrets/istio.default/root-cert.pem 18 | certFile: /etc/prometheus/secrets/istio.default/cert-chain.pem 19 | keyFile: /etc/prometheus/secrets/istio.default/key.pem 20 | insecureSkipVerify: true # Prometheus does not support Istio security naming, thus skip verifying target Pod certificate 21 | namespaceSelector: 22 | any: true 23 | -------------------------------------------------------------------------------- /event-email-service/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME = event-email-service 2 | IMG = $(DOCKER_PUSH_REPOSITORY)$(DOCKER_PUSH_DIRECTORY)/$(APP_NAME) 3 | TAG = $(DOCKER_TAG) 4 | 5 | resolve: 6 | npm install --no-optional 7 | 8 | validate: 9 | eslint -c .eslinterrc ./src 10 | 11 | test: 12 | npm run-script test 13 | 14 | scan: 15 | sed -i -e 's/APIKEY/$(API_KEY)/g; s/USERKEY/$(USER_KEY)/g' /whitesource.config.json && cp /whitesource.config.json ./whitesource.config.json 16 | whitesource run 17 | 18 | .PHONY: build-image 19 | build-image: 20 | docker build -t $(APP_NAME):latest . 21 | 22 | .PHONY: push-image 23 | push-image: 24 | docker tag $(APP_NAME) $(IMG):$(TAG) 25 | docker push $(IMG):$(TAG) 26 | 27 | .PHONY: ci-pr 28 | ci-pr: resolve validate test build-image push-image 29 | 30 | .PHONY: ci-main 31 | ci-main: resolve validate test build-image push-image 32 | -------------------------------------------------------------------------------- /event-email-service/tests/tests.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const chaiHttp = require('chai-http'); 3 | const app = require('../src/app'); 4 | const should = chai.should(); 5 | 6 | chai.use(chaiHttp); 7 | 8 | it('Request without body', function(done) { 9 | chai.request(app) 10 | .post('/v1/events/register') 11 | .end(function(err, res){ 12 | res.should.have.status(400); 13 | done(); 14 | }); 15 | }); 16 | 17 | it('Request with correct body', function(done) { 18 | chai.request(app) 19 | .post('/v1/events/register') 20 | .send({"event":{"customer":{"customerID": "1234", "uid": "rick.sanchez@mail.com"}}}) 21 | .end(function(err, res){ 22 | res.should.have.status(200); 23 | done(); 24 | }); 25 | }); 26 | 27 | after(async () => { 28 | app.stop(); 29 | }); 30 | -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/queries.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import "regexp" 4 | 5 | const ( 6 | 7 | sqlServerTableCreationQuery = `IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{name}') 8 | BEGIN 9 | CREATE TABLE {name} ( 10 | order_id VARCHAR(64), 11 | namespace VARCHAR(64), 12 | total DECIMAL(8,2), 13 | PRIMARY KEY (order_id, namespace) 14 | ) 15 | END` 16 | 17 | // PrimaryKeyViolation is the SQL code used by MsSql to indicate an attempt 18 | // to insert an entry which violates the primary key constraint. 19 | PrimaryKeyViolation = 2627 20 | ) 21 | 22 | var safeSQLRegex = regexp.MustCompile(`[^a-zA-Z0-9\.\-_]`) 23 | 24 | // SanitizeSQLArg returns the input string sanitized for safe use in an SQL query as argument. 25 | func SanitizeSQLArg(s string) string { 26 | return safeSQLRegex.ReplaceAllString(s, "") 27 | } 28 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Label to use when marking an issue as stale 6 | staleLabel: lifecycle/stale 7 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 8 | exemptLabels: 9 | - lifecycle/frozen 10 | - lifecycle/active 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale due to the lack of recent activity. It will soon be closed if no further activity occurs. Thank you for your contributions. 14 | # Comment to post when closing a stale issue. Set to `false` to disable 15 | closeComment: > 16 | This issue has been automatically closed due to the lack of recent activity. 17 | /lifecycle rotten 18 | -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/queries_test.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSanitizeSQLArg(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | input string 13 | output string 14 | }{ 15 | { 16 | name: "NothingToSanitize", 17 | input: "Catalog.Table-1", 18 | output: "Catalog.Table-1", 19 | }, 20 | { 21 | name: "SanitizeForbiddenCharacters", 22 | input: "Table ,;!?%(·", 23 | output: "Table", 24 | }, 25 | { 26 | name: "EmptyInput", 27 | input: "", 28 | output: "", 29 | }, 30 | { 31 | name: "OnlyForbiddenCharacters", 32 | input: " ,;!?%(·", 33 | output: "", 34 | }, 35 | } 36 | 37 | for _, test := range tests { 38 | t.Run(test.name, func(t *testing.T) { 39 | assert.Equal(t, test.output, SanitizeSQLArg(test.input)) 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/factory.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | const ( 8 | // MemoryDatabase value can be used to start the service using an in-memory DB. See Service/DbType. 9 | MemoryDatabase = "memory" 10 | // SQLServerDriverName value can be used to start the service using an external MsSql DB. See Service/DbType. 11 | SQLServerDriverName = "mssql" 12 | ) 13 | 14 | // Create is used to create an OrderRepository based on the given dbtype. 15 | // Currently the `MemoryDatabase` and `SQLServerDriverName` are supported. 16 | func Create(dbtype string) (OrderRepository, error) { 17 | switch dbtype { 18 | case MemoryDatabase: 19 | return NewOrderRepositoryMemory(), nil 20 | case SQLServerDriverName: 21 | return NewOrderRepositoryDb() 22 | default: 23 | return nil, errors.Errorf("Unsupported database type %s", dbtype) 24 | } 25 | } -------------------------------------------------------------------------------- /http-db-service/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME = http-db-service 2 | IMG = $(DOCKER_PUSH_REPOSITORY)$(DOCKER_PUSH_DIRECTORY)/$(APP_NAME) 3 | TAG = $(DOCKER_TAG) 4 | 5 | resolve: 6 | dep ensure --vendor-only -v 7 | 8 | validate: 9 | curl https://raw.githubusercontent.com/alecthomas/gometalinter/master/scripts/install.sh | sh -s v2.0.8 10 | ./bin/gometalinter --skip=generated --vendor --deadline=2m --disable-all ./... 11 | 12 | build: 13 | go generate ./... 14 | CGO_ENABLED=0 go build -o ./bin/app $(buildpath) 15 | 16 | test-report: 17 | 2>&1 go test -v ./... | go2xunit -fail -output unit-tests.xml 18 | 19 | .PHONY: build-image 20 | build-image: 21 | docker build -t $(APP_NAME):latest . 22 | 23 | .PHONY: push-image 24 | push-image: 25 | docker tag $(APP_NAME) $(IMG):$(TAG) 26 | docker push $(IMG):$(TAG) 27 | 28 | .PHONY: ci-pr 29 | ci-pr: resolve validate build-image push-image 30 | 31 | .PHONY: ci-main 32 | ci-main: resolve validate build-image push-image 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-improvement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation improvement 3 | about: Suggest an improvement or report a bug in the documentation 4 | --- 5 | 6 | 11 | 12 | **Description** 13 | 14 | 15 | 16 | **Area** 17 | 18 | 22 | 23 | **Reasons** 24 | 25 | 26 | 27 | **Assignees** 28 | 29 | @kyma-project/technical-writers 30 | 31 | **Attachments** 32 | 33 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/api.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "errors" 4 | 5 | // Order contains the details of an order entity. 6 | type Order struct { 7 | OrderId string `json:"orderId"` 8 | Namespace string `json:"namespace"` 9 | Total float64 `json:"total"` 10 | } 11 | 12 | // OrderRepository interface defines the basic operations needed for the order service 13 | // 14 | //go:generate mockery -name OrderRepository -inpkg 15 | type OrderRepository interface { 16 | InsertOrder(o Order) error 17 | GetOrders() ([]Order, error) 18 | GetNamespaceOrders(ns string) ([]Order, error) 19 | DeleteOrders() error 20 | DeleteNamespaceOrders(ns string) error 21 | cleanUp() error 22 | } 23 | 24 | // ErrDuplicateKey is thrown when there is an attempt to create an order with an OrderId which already is used. 25 | var ErrDuplicateKey = errors.New("Duplicate key") 26 | 27 | type OrderCreatedEvent struct { 28 | OrderCode string `json:"orderCode"` 29 | } 30 | -------------------------------------------------------------------------------- /orders-service/deployment/orders-service-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: orders-service 5 | namespace: orders-service 6 | labels: 7 | app: orders-service 8 | example: orders-service 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: orders-service 14 | example: orders-service 15 | template: 16 | metadata: 17 | labels: 18 | app: orders-service 19 | example: orders-service 20 | spec: 21 | containers: 22 | - name: orders-service 23 | image: "eu.gcr.io/kyma-project/develop/orders-service:e8175c63" 24 | imagePullPolicy: IfNotPresent 25 | resources: 26 | limits: 27 | cpu: 20m 28 | memory: 32Mi 29 | requests: 30 | cpu: 10m 31 | memory: 16Mi 32 | env: 33 | - name: APP_PORT 34 | value: "8080" 35 | - name: APP_REDIS_PREFIX 36 | value: "REDIS_" -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/factory_test.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | _ "github.com/lib/pq" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | "github.com/vrischmann/envconfig" 13 | ) 14 | 15 | func TestFactory(t *testing.T) { 16 | if os.Getenv("Host") == "" { 17 | t.Skip("skipping test; DB Config not set") 18 | } 19 | 20 | var dbCfg Config 21 | assert.NoError(t, envconfig.Init(&dbCfg)) 22 | // when initiating 23 | db, err := InitDb(dbCfg) 24 | require.NoError(t, err) 25 | defer db.Close() 26 | 27 | // then 28 | rows, err := db.Query("SELECT 1 FROM sysobjects WHERE xtype = 'U' AND name = ?", dbCfg.DbOrdersTableName) 29 | require.NoError(t, err) 30 | defer rows.Close() 31 | // check that table exists 32 | assert.True(t, rows.Next()) 33 | 34 | //cleanup 35 | dropTable(db, dbCfg.DbOrdersTableName) 36 | } 37 | 38 | func dropTable(db *sql.DB, tableName string) { 39 | db.Exec(fmt.Sprintf("DROP TABLE %s", tableName)) 40 | } 41 | -------------------------------------------------------------------------------- /orders-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.14-alpine as builder 2 | 3 | ENV BASE_APP_DIR=/go/src/github.com/kyma-project/examples/orders-service \ 4 | CGO_ENABLED=0 \ 5 | GOOS=linux \ 6 | GOARCH=amd64 7 | 8 | WORKDIR ${BASE_APP_DIR} 9 | 10 | COPY ./go.mod . 11 | COPY ./go.sum . 12 | 13 | # cache deps before building and copying source so that we don't need to re-download as much 14 | # and so that source changes don't invalidate our downloaded layer 15 | RUN go mod download 16 | 17 | # 18 | # copy files allowed in .dockerignore 19 | # 20 | COPY . ${BASE_APP_DIR}/ 21 | 22 | RUN go build -ldflags "-s -w" -a -o main cmd/main.go \ 23 | && mkdir /app \ 24 | && mv ./main /app/main 25 | 26 | # get latest CA certs 27 | FROM alpine:latest as certs 28 | RUN apk --update add ca-certificates 29 | 30 | # result container 31 | FROM alpine:latest 32 | 33 | LABEL source = git@github.com:kyma-project/examples.git 34 | 35 | COPY --from=builder /app /app 36 | COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 37 | 38 | ENTRYPOINT ["/app/main"] 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug in the project 4 | --- 5 | 6 | 10 | 11 | **Description** 12 | 13 | 15 | 16 | 17 | 18 | **Expected result** 19 | 20 | 21 | 22 | **Actual result** 23 | 24 | 25 | 26 | **Steps to reproduce** 27 | 28 | 29 | 30 | **Troubleshooting** 31 | 32 | 33 | -------------------------------------------------------------------------------- /http-db-service/config/service.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Service struct is used for configuring how the service will run 9 | // by reading the values from the environment or using the default values. 10 | type Service struct { 11 | Port string `envconfig:"serviceport,default=8017" json:"Port"` 12 | // DbType set to 'mssql' will start the service using an MsSql datbase 13 | // and it will require extra configuration. See https://github.com/kyma-project/examples/blob/main/http-db-service/internal/mssqldb/config.go 14 | DbType string `envconfig:"dbtype,default=memory" json:"DBType"` // [memory | mssql] 15 | } 16 | 17 | // String returns a printable representation of the config as JSON. 18 | // Use the struct field tag `json:"-"` to hide fields that should not be revealed such as credentials and secrets. 19 | func (s Service) String() string { 20 | json, err := json.Marshal(s) 21 | if err != nil { 22 | return fmt.Sprintf("Error marshalling service configuration JSON: %v", err) 23 | } 24 | return fmt.Sprintf("Service Configuration: %s", json) 25 | } 26 | -------------------------------------------------------------------------------- /orders-service/chart/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /http-db-service/handler/events/order_created.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | log "github.com/Sirupsen/logrus" 7 | "github.com/kyma-project/examples/http-db-service/handler/response" 8 | "github.com/kyma-project/examples/http-db-service/internal/repository" 9 | "io/ioutil" 10 | "net/http" 11 | ) 12 | 13 | func HandleOrderCreatedEvent(w http.ResponseWriter, r *http.Request) { 14 | b, err := ioutil.ReadAll(r.Body) 15 | 16 | if err != nil { 17 | log.Error("error parsing request", err) 18 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 19 | return 20 | } 21 | 22 | defer r.Body.Close() 23 | 24 | var event repository.OrderCreatedEvent 25 | err = json.Unmarshal(b, &event) 26 | 27 | if err != nil || event.OrderCode == "" { 28 | response.WriteCodeAndMessage(http.StatusBadRequest, "Invalid request body, orderCode cannot be empty.", w) 29 | return 30 | } 31 | fmt.Println("handle order create called.") 32 | 33 | log.Infof("Handling event '%+v' with my custom logic..", event) 34 | //here add any custom logic such as sending an email, gather insights into purchase. 35 | 36 | w.WriteHeader(http.StatusOK) 37 | } 38 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/deployment/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: sample-metrics 6 | labels: 7 | app: sample-metrics 8 | example: monitoring-custom-metrics 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: sample-metrics 14 | example: monitoring-custom-metrics 15 | template: 16 | metadata: 17 | labels: 18 | app: sample-metrics 19 | example: monitoring-custom-metrics 20 | spec: 21 | containers: 22 | - name: sample-metrics 23 | image: europe-docker.pkg.dev/kyma-project/prod/examples/monitoring-custom-metrics:v20230912-0845948c 24 | imagePullPolicy: IfNotPresent 25 | resources: 26 | limits: 27 | memory: 100Mi 28 | requests: 29 | memory: 32Mi 30 | ports: 31 | - name: http 32 | containerPort: 8080 33 | --- 34 | kind: Service 35 | apiVersion: v1 36 | metadata: 37 | name: sample-metrics 38 | labels: 39 | app: sample-metrics 40 | example: monitoring-custom-metrics 41 | spec: 42 | selector: 43 | app: sample-metrics 44 | ports: 45 | - name: http 46 | port: 8080 47 | protocol: TCP 48 | -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/config.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Config is a struct used for configuring the connection and the usage of the database. 9 | // It contains the information needed to form a connection string and the name of the table used for storing the data. 10 | type Config struct { 11 | Name string `envconfig:"database,default=orderservice" json:"Name"` 12 | Host string `envconfig:"host,default=127.0.0.1" json:"Host"` 13 | Port int `envconfig:"port,default=1433" json:"Port"` 14 | User string `envconfig:"username,default=test" json:"User"` 15 | Pass string `envconfig:"password,default=test" json:"-"` // hidden from logging 16 | DbOrdersTableName string `envconfig:"tablename,default=orders" json:"OrdersTable"` 17 | } 18 | 19 | // String returns a printable representation of the config as JSON. 20 | // Use the struct field tag `json:"-"` to hide fields that should not be revealed such as credentials and secrets. 21 | func (config Config) String() string { 22 | json, err := json.Marshal(config) 23 | if err != nil { 24 | return fmt.Sprintf("Error marshalling DB configuration JSON: %v", err) 25 | } 26 | return fmt.Sprintf("DB Configuration: %s", json) 27 | } 28 | -------------------------------------------------------------------------------- /http-db-service/handler/api_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/http/httptest" 8 | "os" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | 13 | "github.com/gorilla/mux" 14 | ) 15 | 16 | func TestSwaggerAPIHandler(t *testing.T) { 17 | // fake api file 18 | ioutil.WriteFile("api.yaml", []byte("API Specs"), 0644) 19 | defer func() { assert.NoError(t, os.Remove("api.yaml")) }() 20 | 21 | router := mux.NewRouter() 22 | router.HandleFunc("/api.yaml", SwaggerAPIHandler) 23 | ts := httptest.NewServer(router) 24 | defer ts.Close() 25 | 26 | resp, err := http.Get(fmt.Sprintf("%s/api.yaml", ts.URL)) 27 | assert.NoError(t, err) 28 | assert.Equal(t, http.StatusOK, resp.StatusCode) 29 | b, err := ioutil.ReadAll(resp.Body) 30 | assert.NoError(t, err) 31 | assert.NotEmpty(t, b) 32 | } 33 | 34 | func TestSwaggerAPIRedirectHandler(t *testing.T) { 35 | router := mux.NewRouter() 36 | router.HandleFunc("/", SwaggerAPIRedirectHandler) 37 | ts := httptest.NewServer(router) 38 | defer ts.Close() 39 | 40 | resp, err := http.Get(ts.URL) 41 | assert.NoError(t, err) 42 | assert.Equal(t, http.StatusOK, resp.StatusCode) 43 | // check that redirect goes to editor.swagger.io 44 | assert.Contains(t, resp.Request.URL.String(), "editor.swagger.io") 45 | } 46 | -------------------------------------------------------------------------------- /event-email-service/deployment/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: event-email-service 5 | labels: 6 | example: event-email-service 7 | app: event-email-service 8 | spec: 9 | ports: 10 | - name: http 11 | port: 3000 12 | selector: 13 | app: event-email-service 14 | example: event-email-service 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: event-email-service 20 | labels: 21 | app: event-email-service 22 | example: event-email-service 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: event-email-service 28 | example: event-email-service 29 | template: 30 | metadata: 31 | labels: 32 | app: event-email-service 33 | example: event-email-service 34 | spec: 35 | containers: 36 | # replace the repository URL with your own repository (e.g. {DockerID}/event-email-service:0.0.x for Docker Hub). 37 | - image: eu.gcr.io/kyma-project/example/event-email-service:0.0.2 38 | imagePullPolicy: IfNotPresent 39 | name: event-email-service 40 | ports: 41 | - name: http 42 | containerPort: 3000 43 | resources: 44 | limits: 45 | memory: 100Mi 46 | requests: 47 | memory: 32Mi 48 | -------------------------------------------------------------------------------- /kiali/values.yaml: -------------------------------------------------------------------------------- 1 | cr: 2 | create: true 3 | name: kiali-server 4 | spec: 5 | auth: {} 6 | # configure the authentication strategy 7 | # strategy: token | anonymous | header | .. 8 | server: 9 | observability: 10 | tracing: 11 | collector_url: "" 12 | enabled: false 13 | deployment: 14 | instance_name: "kiali-server" 15 | accessible_namespaces: 16 | - '**' 17 | pod_labels: 18 | sidecar.istio.io/inject: "true" # for Jaeger access 19 | resources: 20 | limits: 21 | cpu: 250m 22 | memory: 256Mi 23 | requests: 24 | cpu: 10m 25 | memory: 64Mi 26 | istio_namespace: "istio-system" # default is where Kiali is installed 27 | external_services: 28 | istio: 29 | component_status: 30 | components: 31 | - app_label: istiod 32 | is_core: true 33 | is_proxy: false 34 | - app_label: istio-ingressgateway 35 | is_core: true 36 | is_proxy: true 37 | grafana: 38 | enabled: false 39 | prometheus: {} 40 | # set prometheus url via command line args 41 | # url: "" 42 | tracing: 43 | enabled: false 44 | kiali_feature_flags: 45 | ui_defaults: 46 | metrics_per_refresh: "10m" 47 | -------------------------------------------------------------------------------- /orders-service/internal/store/redis_store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-redis/redis/v8" 7 | ) 8 | 9 | type Redis struct { 10 | client *redis.Client 11 | } 12 | 13 | func NewRedis(client *redis.Client) *Redis { 14 | return &Redis{ 15 | client: client, 16 | } 17 | } 18 | 19 | func (m *Redis) Add(ctx context.Context, key, value string) error { 20 | if m.client.Get(ctx, key).Err() == nil { 21 | return AlreadyExistsError 22 | } 23 | return m.client.Set(ctx, key, value, 0).Err() 24 | } 25 | 26 | func (m *Redis) Get(ctx context.Context, key string) (string, error) { 27 | value, err := m.client.Get(ctx, key).Result() 28 | if err == redis.Nil { 29 | return "", NotFoundError 30 | } 31 | return value, err 32 | } 33 | 34 | func (m *Redis) Keys(ctx context.Context, pattern string) ([]string, error) { 35 | return m.client.Keys(ctx, pattern).Result() 36 | } 37 | 38 | func (m *Redis) Delete(ctx context.Context, key string) error { 39 | if m.client.Get(ctx, key).Err() == redis.Nil { 40 | return NotFoundError 41 | } 42 | return m.client.Del(ctx, key).Err() 43 | } 44 | 45 | func (m *Redis) Clear(ctx context.Context) error { 46 | if err := m.client.FlushDB(ctx).Err(); err != redis.Nil { 47 | return err 48 | } 49 | return nil 50 | } 51 | 52 | func (m *Redis) GetStoreType(ctx context.Context) string { 53 | return "REDIS" 54 | } 55 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file provides an overview of code owners in the `examples` repository. 2 | 3 | # Each line is a file pattern followed by one or more owners. 4 | # The last matching pattern has the most precedence. 5 | # For more details, read the following article on GitHub: https://help.github.com/articles/about-codeowners/. 6 | 7 | # These are the default owners for the whole content of the `examples` repository. The default owners are automatically added as reviewers when you open a pull request, unless different owners are specified in the file. 8 | * @pbochynski @PK85 @a-thaler 9 | 10 | # Service binding example 11 | /service-binding/ @rakesh-garimella @lilitgh @k15r @nachtmaar @marcobebway 12 | 13 | # Orders service example 14 | /orders-service/ @m00g3n @pPrecel 15 | 16 | # In-cluster eventing using function example 17 | /incluster_eventing/ @m00g3n @pPrecel @dbadura @kwiatekus @cortey 18 | 19 | # Custom function runtime image example 20 | /custom-serverless-runtime-image/ @kyma-project/otters 21 | 22 | # Observability 23 | /jaeger/ @kyma-project/observability 24 | /kiali/ @kyma-project/observability 25 | /loki/ @kyma-project/observability 26 | /prometheus/ @kyma-project/observability 27 | /trace-demo/ @kyma-project/observability 28 | 29 | # Scaling functions to zero with KEDA 30 | /scale-to-zero-with-keda @kyma-project/otters 31 | 32 | # All .md files 33 | *.md @kyma-project/technical-writers 34 | -------------------------------------------------------------------------------- /gateway/service/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: http-db-service 5 | labels: 6 | example: gateway-service 7 | app: http-db-service 8 | spec: 9 | ports: 10 | - name: http 11 | port: 8017 12 | selector: 13 | app: http-db-service 14 | example: gateway-service 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: http-db-service 20 | labels: 21 | example: gateway-service 22 | app: http-db-service 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: http-db-service 28 | example: gateway-service 29 | template: 30 | metadata: 31 | labels: 32 | app: http-db-service 33 | example: gateway-service 34 | spec: 35 | containers: 36 | # replace the repository URL with your own repository (e.g. {DockerID}/http-db-service:0.0.x for Docker Hub). 37 | - image: eu.gcr.io/kyma-project/example/http-db-service:0.0.6 38 | imagePullPolicy: IfNotPresent 39 | name: http-db-service 40 | ports: 41 | - name: http 42 | containerPort: 8017 43 | resources: 44 | limits: 45 | memory: 100Mi 46 | requests: 47 | memory: 32Mi 48 | env: 49 | - name: dbtype 50 | # available dbtypes are: [memory, mssql] 51 | value: "memory" 52 | 53 | 54 | -------------------------------------------------------------------------------- /http-db-service/deployment/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: http-db-service 5 | labels: 6 | example: http-db-service 7 | app: http-db-service 8 | spec: 9 | ports: 10 | - name: http 11 | port: 8017 12 | selector: 13 | app: http-db-service 14 | example: http-db-service 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: http-db-service 20 | labels: 21 | example: http-db-service 22 | app: http-db-service 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: http-db-service 28 | example: http-db-service 29 | template: 30 | metadata: 31 | labels: 32 | app: http-db-service 33 | example: http-db-service 34 | spec: 35 | containers: 36 | # replace the repository URL with your own repository (e.g. {DockerID}/http-db-service:0.0.x for Docker Hub). 37 | - image: eu.gcr.io/kyma-project/develop/http-db-service:47d43e19 38 | imagePullPolicy: IfNotPresent 39 | name: http-db-service 40 | ports: 41 | - name: http 42 | containerPort: 8017 43 | resources: 44 | limits: 45 | memory: 100Mi 46 | requests: 47 | memory: 32Mi 48 | env: 49 | - name: dbtype 50 | # available dbtypes are: [memory, mssql] 51 | value: "memory" 52 | 53 | 54 | -------------------------------------------------------------------------------- /orders-service/chart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "fullname" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | app: {{ template "fullname" . }} 8 | example: {{ template "fullname" . }} 9 | release: {{ .Release.Name }} 10 | spec: 11 | replicas: {{ .Values.deployment.replicas }} 12 | selector: 13 | matchLabels: 14 | app: {{ template "fullname" . }} 15 | example: {{ template "fullname" . }} 16 | release: {{ .Release.Name }} 17 | template: 18 | metadata: 19 | labels: 20 | app: {{ template "fullname" . }} 21 | example: {{ template "fullname" . }} 22 | release: {{ .Release.Name }} 23 | spec: 24 | containers: 25 | - name: {{ template "fullname" . }} 26 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 27 | imagePullPolicy: {{ .Values.image.pullPolicy }} 28 | resources: 29 | limits: 30 | cpu: {{ .Values.pod.resources.limits.cpu }} 31 | memory: {{ .Values.pod.resources.limits.memory }} 32 | requests: 33 | cpu: {{ .Values.pod.resources.requests.cpu }} 34 | memory: {{ .Values.pod.resources.requests.memory }} 35 | env: 36 | - name: APP_PORT 37 | value: "{{ .Values.pod.env.appPort }}" 38 | - name: APP_REDIS_PREFIX 39 | value: "{{ .Values.pod.env.redisPrefix }}" 40 | -------------------------------------------------------------------------------- /orders-service/internal/handler/webhook_handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/kyma-project/examples/orders-service/internal/service" 8 | "github.com/kyma-project/examples/orders-service/internal/service/model" 9 | "github.com/kyma-project/examples/orders-service/internal/store" 10 | "io/ioutil" 11 | "log" 12 | "net/http" 13 | ) 14 | 15 | type Webhook struct { 16 | svc *service.Order 17 | } 18 | 19 | func NewWebhook(svc *service.Order) *Webhook { 20 | return &Webhook{ 21 | svc: svc, 22 | } 23 | } 24 | 25 | func (h *Webhook) RegisterAll(root string, router Router) { 26 | router.HandleFunc(fmt.Sprintf("%s", root), h.onHook).Methods(http.MethodPost) 27 | } 28 | 29 | func (h *Webhook) onHook(w http.ResponseWriter, r *http.Request) { 30 | defer r.Body.Close() 31 | ctx, cancel := context.WithCancel(context.Background()) 32 | defer cancel() 33 | 34 | body, err := ioutil.ReadAll(r.Body) 35 | if err != nil { 36 | log.Println(err) 37 | w.WriteHeader(http.StatusBadRequest) 38 | return 39 | } 40 | 41 | order := new(model.Order) 42 | if err := json.Unmarshal(body, order); err != nil { 43 | log.Println(err) 44 | w.WriteHeader(http.StatusBadRequest) 45 | return 46 | } 47 | 48 | err = h.svc.Create(ctx, order) 49 | if err == store.AlreadyExistsError { 50 | w.WriteHeader(http.StatusConflict) 51 | return 52 | } else if err != nil { 53 | w.WriteHeader(http.StatusInternalServerError) 54 | return 55 | } 56 | 57 | w.WriteHeader(http.StatusOK) 58 | } 59 | -------------------------------------------------------------------------------- /http-db-service/internal/mssqldb/factory.go: -------------------------------------------------------------------------------- 1 | package mssqldb 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/pkg/errors" 6 | "strings" 7 | 8 | log "github.com/Sirupsen/logrus" 9 | _ "github.com/denisenkom/go-mssqldb" //MSSQL driver initialization 10 | ) 11 | 12 | const ( 13 | dbDriverName = "mssql" 14 | ) 15 | 16 | // InitDb creates and tests a database connection using the configuration given in dbConfig. 17 | // After it establishes a connection it also ensures that the table exists. 18 | func InitDb(dbConfig Config) (*sql.DB, error) { 19 | connectionURL := newSQLServerConnectionURL( 20 | dbConfig.User, dbConfig.Pass, dbConfig.Host, dbConfig.Name, dbConfig.Port) 21 | createTableQuery := sqlServerTableCreationQuery 22 | 23 | log.Debugf("Establishing connection with '%s'. Connection string: '%q'", dbDriverName, 24 | strings.Replace(connectionURL.String(), connectionURL.User.String() + "@", "***:***@", 1)) 25 | 26 | db, err := sql.Open(dbDriverName, connectionURL.String()) 27 | if err != nil { 28 | return nil, errors.Wrapf(err, "while establishing connection to '%s'", dbDriverName) 29 | } 30 | 31 | log.Debug("Testing connection") 32 | if err := db.Ping(); err != nil { 33 | return nil, errors.Wrap(err, "while testing DB connection") 34 | } 35 | 36 | q := strings.Replace(createTableQuery, "{name}", SanitizeSQLArg(dbConfig.DbOrdersTableName), -1) 37 | log.Debugf("Ensuring table exists. Running query: '%q'.", q) 38 | if _, err := db.Exec(q); err != nil { 39 | return nil, errors.Wrap(err, "while initiating DB table") 40 | } 41 | 42 | return db, nil 43 | } 44 | -------------------------------------------------------------------------------- /event-email-service/src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | //const tracing = require('./tracing.js'); 4 | const app = express(); 5 | const port = 3000; 6 | 7 | app.use(bodyParser.urlencoded({ extended: false })); 8 | app.use(bodyParser.json()); 9 | 10 | // Listen to events and send an email 11 | app.post('/v1/events/register', (req, res) => { 12 | let uid = getCustomerID(req.body); 13 | let email = getCustomerEmail(req.body); 14 | 15 | if (uid === undefined || email === undefined) { 16 | console.log('No customer ID or Email received!'); 17 | res.sendStatus(400); 18 | } else { 19 | console.log('Customer created with ID: ' + uid + ' and Email: ' + email); 20 | res.sendStatus(200); 21 | } 22 | 23 | /* 24 | TODO: 25 | - propagate tracing headers to email service: tracing.propagateTracingHeaders(req.headers, req) 26 | - send email to event.customer.uid 27 | */ 28 | }); 29 | 30 | var server = app.listen(port, () => 31 | console.log('Example app listening on port ' + port + '!') 32 | ); 33 | 34 | app.stop = function() { 35 | server.close(); 36 | }; 37 | 38 | module.exports = app; 39 | 40 | function getCustomerID(body) { 41 | if (body.event === undefined || body.event.customer === undefined) { 42 | return undefined; 43 | } 44 | return body.event.customer.customerID; 45 | } 46 | 47 | function getCustomerEmail(body) { 48 | if (body.event === undefined || body.event.customer === undefined) { 49 | return undefined; 50 | } 51 | return body.event.customer.uid; 52 | } 53 | -------------------------------------------------------------------------------- /http-db-service/handler/events/order_created_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/gorilla/mux" 7 | "github.com/kyma-project/examples/http-db-service/internal/repository" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | "net/http" 11 | "net/http/httptest" 12 | "testing" 13 | ) 14 | 15 | const path = "/events/order/created" 16 | 17 | func TestHandleOrderCreatedEventSuccess(t *testing.T) { 18 | router := mux.NewRouter() 19 | router.HandleFunc(path, HandleOrderCreatedEvent).Methods(http.MethodPost) 20 | 21 | ts := httptest.NewServer(router) 22 | defer ts.Close() 23 | 24 | event := repository.OrderCreatedEvent{OrderCode: "handle-success"} 25 | requestBody := new(bytes.Buffer) 26 | json.NewEncoder(requestBody).Encode(event) 27 | 28 | res, err := http.Post(ts.URL+path, "application/json", requestBody) 29 | require.NoError(t, err) 30 | 31 | assert.Equal(t, http.StatusOK, res.StatusCode) 32 | } 33 | 34 | func TestHandleOrderCreatedEventBadPayload(t *testing.T) { 35 | router := mux.NewRouter() 36 | router.HandleFunc(path, HandleOrderCreatedEvent).Methods(http.MethodPost) 37 | 38 | ts := httptest.NewServer(router) 39 | defer ts.Close() 40 | 41 | badEvent := struct { 42 | OrderId string 43 | }{OrderId: "handle-400"} 44 | 45 | requestBody := new(bytes.Buffer) 46 | json.NewEncoder(requestBody).Encode(badEvent) 47 | 48 | res, err := http.Post(ts.URL+path, "application/json", requestBody) 49 | require.NoError(t, err) 50 | 51 | assert.Equal(t, http.StatusBadRequest, res.StatusCode) 52 | } 53 | -------------------------------------------------------------------------------- /prometheus/alertmanager-values.yaml: -------------------------------------------------------------------------------- 1 | alertmanager: 2 | config: 3 | global: 4 | resolve_timeout: 5m 5 | inhibit_rules: 6 | - source_matchers: 7 | - 'severity = critical' 8 | target_matchers: 9 | - 'severity =~ warning|info' 10 | equal: 11 | - 'namespace' 12 | - 'alertname' 13 | - source_matchers: 14 | - 'severity = warning' 15 | target_matchers: 16 | - 'severity = info' 17 | equal: 18 | - 'namespace' 19 | - 'alertname' 20 | - source_matchers: 21 | - 'alertname = InfoInhibitor' 22 | target_matchers: 23 | - 'severity = info' 24 | equal: 25 | - 'namespace' 26 | route: 27 | group_by: ['namespace'] 28 | group_wait: 30s 29 | group_interval: 5m 30 | repeat_interval: 12h 31 | receiver: 'null' 32 | routes: 33 | - receiver: 'null' 34 | matchers: 35 | - alertname =~ "InfoInhibitor|Watchdog" 36 | - continue: true 37 | match_re: 38 | severity: critical 39 | receiver: "slack" 40 | receivers: 41 | - name: 'null' 42 | - name: "slack" 43 | slack_configs: 44 | - channel: "" 45 | send_resolved: true 46 | api_url: "" 47 | icon_emoji: ":ghost:" 48 | title: '{{ template "__subject" . }}' 49 | title_link: 'https://grafana.' 50 | text: '{{ range .Alerts }}`}}{{`{{- "\n" -}}`}}{{`{{ .Annotations.description }}`}}{{`{{- "\n" -}}`}}{{`{{ end }}' 51 | templates: 52 | - '/etc/alertmanager/config/*.tmpl' 53 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kyma-project/examples/monitoring-custom-metrics 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/prometheus/client_golang v1.0.0 7 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 8 | go.opentelemetry.io/otel v1.16.0 9 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 10 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 11 | ) 12 | 13 | require ( 14 | github.com/beorn7/perks v1.0.1 // indirect 15 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 16 | github.com/felixge/httpsnoop v1.0.3 // indirect 17 | github.com/go-logr/logr v1.2.4 // indirect 18 | github.com/go-logr/stdr v1.2.2 // indirect 19 | github.com/golang/protobuf v1.5.3 // indirect 20 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect 21 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 22 | github.com/prometheus/client_model v0.1.0 // indirect 23 | github.com/prometheus/common v0.7.0 // indirect 24 | github.com/prometheus/procfs v0.0.8 // indirect 25 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect 26 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 // indirect 27 | go.opentelemetry.io/otel/metric v1.16.0 // indirect 28 | go.opentelemetry.io/otel/sdk v1.16.0 // indirect 29 | go.opentelemetry.io/otel/trace v1.16.0 // indirect 30 | go.opentelemetry.io/proto/otlp v0.19.0 // indirect 31 | golang.org/x/net v0.8.0 // indirect 32 | golang.org/x/sys v0.8.0 // indirect 33 | golang.org/x/text v0.8.0 // indirect 34 | google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect 35 | google.golang.org/grpc v1.55.0 // indirect 36 | google.golang.org/protobuf v1.30.0 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /orders-service/internal/store/memory_store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "path/filepath" 6 | "sync" 7 | ) 8 | 9 | type Memory struct { 10 | store map[string]string 11 | mutex *sync.RWMutex 12 | } 13 | 14 | func NewMemory() *Memory { 15 | return &Memory{ 16 | store: make(map[string]string), 17 | mutex: &sync.RWMutex{}, 18 | } 19 | } 20 | 21 | func (m *Memory) Add(_ context.Context, key, value string) error { 22 | m.mutex.Lock() 23 | defer m.mutex.Unlock() 24 | 25 | if _, ok := m.store[key]; ok { 26 | return AlreadyExistsError 27 | } 28 | 29 | m.store[key] = value 30 | 31 | return nil 32 | } 33 | 34 | func (m *Memory) Get(_ context.Context, key string) (string, error) { 35 | m.mutex.RLock() 36 | defer m.mutex.RUnlock() 37 | 38 | if value, ok := m.store[key]; ok { 39 | return value, nil 40 | } 41 | 42 | return "", NotFoundError 43 | } 44 | 45 | func (m *Memory) Keys(_ context.Context, pattern string) ([]string, error) { 46 | m.mutex.RLock() 47 | defer m.mutex.RUnlock() 48 | 49 | keys := make([]string, 0, len(m.store)) 50 | for key, _ := range m.store { 51 | if matched, err := filepath.Match(pattern, key); err != nil { 52 | return nil, err 53 | } else if matched { 54 | keys = append(keys, key) 55 | } 56 | } 57 | 58 | return keys, nil 59 | } 60 | 61 | func (m *Memory) Delete(_ context.Context, key string) error { 62 | m.mutex.Lock() 63 | defer m.mutex.Unlock() 64 | 65 | if _, ok := m.store[key]; !ok { 66 | return NotFoundError 67 | } 68 | 69 | delete(m.store, key) 70 | return nil 71 | } 72 | 73 | func (m *Memory) Clear(_ context.Context) error { 74 | m.mutex.Lock() 75 | defer m.mutex.Unlock() 76 | 77 | m.store = make(map[string]string) 78 | 79 | return nil 80 | } 81 | 82 | func (m *Memory) GetStoreType(ctx context.Context) string { 83 | return "IN_MEMORY" 84 | } 85 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/memory.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type orderRepositoryMemory struct { 8 | Orders map[string]Order 9 | } 10 | 11 | // NewOrderRepositoryMemory is used to instantiate and return the DB implementation of the OrderRepository. 12 | func NewOrderRepositoryMemory() OrderRepository { 13 | return &orderRepositoryMemory{Orders: make(map[string]Order)} 14 | } 15 | 16 | func (repository *orderRepositoryMemory) InsertOrder(order Order) error { 17 | id := mapID(order) 18 | if _, exists := repository.Orders[id]; exists { 19 | return ErrDuplicateKey 20 | } 21 | repository.Orders[id] = order 22 | return nil 23 | } 24 | 25 | func (repository *orderRepositoryMemory) GetOrders() ([]Order, error) { 26 | ret := make([]Order, 0, len(repository.Orders)) 27 | for _, order := range repository.Orders { 28 | ret = append(ret, order) 29 | } 30 | return ret, nil 31 | } 32 | 33 | func (repository *orderRepositoryMemory) GetNamespaceOrders(ns string) ([]Order, error) { 34 | ret := make([]Order, 0, len(repository.Orders)) 35 | for _, order := range repository.Orders { 36 | if order.Namespace == ns { 37 | ret = append(ret, order) 38 | } 39 | } 40 | return ret, nil 41 | } 42 | 43 | func (repository *orderRepositoryMemory) DeleteOrders() error { 44 | repository.Orders = make(map[string]Order) 45 | return nil 46 | } 47 | 48 | func (repository *orderRepositoryMemory) cleanUp() error { 49 | repository.Orders = make(map[string]Order) 50 | return nil 51 | } 52 | 53 | func (repository *orderRepositoryMemory) DeleteNamespaceOrders(ns string) error { 54 | for _, order := range repository.Orders { 55 | if order.Namespace == ns { 56 | delete(repository.Orders, mapID(order)) 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | func mapID(o Order) string { 63 | return fmt.Sprintf("%s-%s", o.OrderId, o.Namespace) 64 | } 65 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: examples 3 | Upstream-Contact: 4 | Source: https://github.com/kyma-project/examples 5 | Disclaimer: The code in this project may include calls to APIs (“API Calls”) of 6 | SAP or third-party products or services developed outside of this project 7 | (“External Products”). 8 | “APIs” means application programming interfaces, as well as their respective 9 | specifications and implementing code that allows software to communicate with 10 | other software. 11 | API Calls to External Products are not licensed under the open source license 12 | that governs this project. The use of such API Calls and related External 13 | Products are subject to applicable additional agreements with the relevant 14 | provider of the External Products. In no event shall the open source license 15 | that governs this project grant any rights in or to any External Products,or 16 | alter, expand or supersede any terms of the applicable additional agreements. 17 | If you have a valid license agreement with SAP for the use of a particular SAP 18 | External Product, then you may make use of any API Calls included in this 19 | project’s code for that SAP External Product, subject to the terms of such 20 | license agreement. If you do not have a valid license agreement for the use of 21 | a particular SAP External Product, then you may only make use of any API Calls 22 | in this project for that SAP External Product for your internal, non-productive 23 | and non-commercial test and evaluation of such API Calls. Nothing herein grants 24 | you any rights to use or access any SAP External Product, or provide any third 25 | parties the right to use of access any SAP External Product, through API Calls. 26 | 27 | Files: * 28 | Copyright: 2023 SAP SE or an SAP affiliate company and Kyma contributors 29 | License: Apache-2.0 30 | -------------------------------------------------------------------------------- /service-binding/service/deployment/http-db-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: http-db-service 5 | labels: 6 | example: service-binding-service 7 | app: http-db-service 8 | spec: 9 | ports: 10 | - name: http 11 | port: 8017 12 | selector: 13 | example: service-binding-service 14 | app: http-db-service 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: http-db-service 20 | labels: 21 | example: service-binding-service 22 | app: http-db-service 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: http-db-service 28 | example: service-binding-service 29 | template: 30 | metadata: 31 | labels: 32 | example: service-binding-service 33 | app: http-db-service 34 | spec: 35 | containers: 36 | # replace the repository URL with your own repository (e.g. {DockerID}/http-db-service:0.0.x for Docker Hub). 37 | - image: eu.gcr.io/kyma-project/example/http-db-service:0.0.6 38 | imagePullPolicy: IfNotPresent 39 | name: http-db-service 40 | ports: 41 | - name: http 42 | containerPort: 8017 43 | env: 44 | - name: dbtype 45 | # available dbtypes are: [memory, mssql] 46 | value: "mssql" 47 | 48 | --- 49 | apiVersion: servicecatalog.k8s.io/v1beta1 50 | kind: ServiceBinding 51 | metadata: 52 | name: mssql-instance-binding 53 | labels: 54 | example: service-binding-service 55 | spec: 56 | instanceRef: 57 | name: mssql-instance 58 | 59 | --- 60 | apiVersion: servicecatalog.kyma-project.io/v1alpha1 61 | kind: ServiceBindingUsage 62 | metadata: 63 | name: mssql-instance-binding-usage 64 | labels: 65 | example: service-binding-service 66 | spec: 67 | serviceBindingRef: 68 | name: mssql-instance-binding 69 | usedBy: 70 | kind: deployment 71 | name: http-db-service -------------------------------------------------------------------------------- /event-email-service/README.md: -------------------------------------------------------------------------------- 1 | # Event Email Service 2 | 3 | ## Overview 4 | 5 | This example illustrates how to write a service in `NodeJS` that listens for Events and sends an automated email to the address in the Event payload. 6 | 7 | ## Prerequisites 8 | 9 | - A [Docker](https://docs.docker.com/install) installation. 10 | - Kyma as the target deployment environment. 11 | 12 | 13 | ## Installation 14 | 15 | ### Local installation 16 | 17 | 1. Build the Docker image: 18 | ```bash 19 | docker build . -t event-email-service:latest 20 | ``` 21 | 22 | 2. Run the recently built image: 23 | ```bash 24 | docker run -p 3000:3000 event-email-service:latest 25 | ``` 26 | 27 | ### Cluster installation 28 | 29 | 1. Export your Namespace as a variable by replacing the **{namespace}** placeholder in the following command and running it: 30 | ```bash 31 | export K8S_NAMESPACE="{namespace}" 32 | ``` 33 | 34 | 2. Deploy the service: 35 | ```bash 36 | kubectl apply -f deployment -n $K8S_NAMESPACE 37 | ``` 38 | 39 | 3. Expose the service endpoint: 40 | ```bash 41 | kubectl port-forward -n $K8S_NAMESPACE $(kubectl get pod -n $K8S_NAMESPACE -l example=event-email-service | grep event-email-service | awk '{print $1}') 3000 42 | ``` 43 | 44 | ### Test the service 45 | 46 | To test the service, simulate an Event using cURL: 47 | 48 | ```bash 49 | curl -H "Content-Type: application/json" -d '{"event":{"customer":{"customerID": "1234", "uid": "rick.sanchez@mail.com"}}}' http://localhost:3000/v1/events/register 50 | ``` 51 | 52 | After sending the Event, you should see a log entry either in your terminal (if running locally) or in the Pod's logs (if running on Kyma) confirming the Event reception. 53 | 54 | ### Cleanup 55 | 56 | Clean all deployed example resources from Kyma with the following command: 57 | 58 | ```bash 59 | kubectl delete all -l example=event-email-service -n $K8S_NAMESPACE 60 | ``` 61 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/mock_dbQuerier.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | package repository 3 | 4 | import mock "github.com/stretchr/testify/mock" 5 | import sql "database/sql" 6 | 7 | // mockDbQuerier is an autogenerated mock type for the dbQuerier type 8 | type mockDbQuerier struct { 9 | mock.Mock 10 | } 11 | 12 | // Close provides a mock function with given fields: 13 | func (_m *mockDbQuerier) Close() error { 14 | ret := _m.Called() 15 | 16 | var r0 error 17 | if rf, ok := ret.Get(0).(func() error); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Error(0) 21 | } 22 | 23 | return r0 24 | } 25 | 26 | // Exec provides a mock function with given fields: query, args 27 | func (_m *mockDbQuerier) Exec(query string, args ...interface{}) (sql.Result, error) { 28 | var _ca []interface{} 29 | _ca = append(_ca, query) 30 | _ca = append(_ca, args...) 31 | ret := _m.Called(_ca...) 32 | 33 | var r0 sql.Result 34 | if rf, ok := ret.Get(0).(func(string, ...interface{}) sql.Result); ok { 35 | r0 = rf(query, args...) 36 | } else { 37 | if ret.Get(0) != nil { 38 | r0 = ret.Get(0).(sql.Result) 39 | } 40 | } 41 | 42 | var r1 error 43 | if rf, ok := ret.Get(1).(func(string, ...interface{}) error); ok { 44 | r1 = rf(query, args...) 45 | } else { 46 | r1 = ret.Error(1) 47 | } 48 | 49 | return r0, r1 50 | } 51 | 52 | // Query provides a mock function with given fields: query, args 53 | func (_m *mockDbQuerier) Query(query string, args ...interface{}) (*sql.Rows, error) { 54 | var _ca []interface{} 55 | _ca = append(_ca, query) 56 | _ca = append(_ca, args...) 57 | ret := _m.Called(_ca...) 58 | 59 | var r0 *sql.Rows 60 | if rf, ok := ret.Get(0).(func(string, ...interface{}) *sql.Rows); ok { 61 | r0 = rf(query, args...) 62 | } else { 63 | if ret.Get(0) != nil { 64 | r0 = ret.Get(0).(*sql.Rows) 65 | } 66 | } 67 | 68 | var r1 error 69 | if rf, ok := ret.Get(1).(func(string, ...interface{}) error); ok { 70 | r1 = rf(query, args...) 71 | } else { 72 | r1 = ret.Error(1) 73 | } 74 | 75 | return r0, r1 76 | } 77 | -------------------------------------------------------------------------------- /service-binding/lambda/deployment/lambda-function.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubeless.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: redis-client 5 | labels: 6 | example: service-binding-lambda 7 | app: service-binding 8 | spec: 9 | handler: handler.main 10 | runtime: nodejs8 11 | type: HTTP 12 | deployment: 13 | spec: 14 | template: 15 | spec: 16 | containers: 17 | - name: redis-client 18 | deps: | 19 | { 20 | "name": "app", 21 | "version": "0.0.1", 22 | "dependencies": { 23 | "redis": "2.8.0", 24 | "double-ended-queue": "2.1.0-0", 25 | "redis-commands": "1.3.1", 26 | "redis-parser": "2.6.0" 27 | } 28 | } 29 | function: | 30 | const redis = require('redis'); 31 | module.exports = { 32 | main: (event, context) => { 33 | const client = redis.createClient({host: process.env.HOST, port: process.env.PORT, password: process.env.REDIS_PASSWORD}); 34 | client.info(function (err, reply) { 35 | if (err) { 36 | return `Error occurred while connecting to redis: ${err} \n`; 37 | } 38 | console.log(`This page was generated after talking to redis.\n\n` + 39 | `Redis info:\n ${reply}\n`); 40 | }); 41 | }, 42 | }; 43 | 44 | --- 45 | apiVersion: servicecatalog.k8s.io/v1beta1 46 | kind: ServiceBinding 47 | metadata: 48 | name: redis-instance-binding 49 | labels: 50 | example: service-binding-lambda 51 | spec: 52 | instanceRef: 53 | name: redis-instance 54 | --- 55 | apiVersion: servicecatalog.kyma-project.io/v1alpha1 56 | kind: ServiceBindingUsage 57 | metadata: 58 | name: fn-redis-client 59 | labels: 60 | example: service-binding-lambda 61 | spec: 62 | serviceBindingRef: 63 | name: redis-instance-binding 64 | usedBy: 65 | kind: function 66 | name: redis-client 67 | --- 68 | apiVersion: gateway.kyma-project.io/v1alpha2 69 | kind: Api 70 | metadata: 71 | labels: 72 | function: redis-client 73 | example: service-binding-lambda 74 | name: service-binding-lambda 75 | spec: 76 | hostname: redis-client 77 | service: 78 | name: redis-client 79 | port: 8080 80 | -------------------------------------------------------------------------------- /prometheus/monitoring-alert-rules/README.md: -------------------------------------------------------------------------------- 1 | # Alert Rules Example 2 | 3 | > [!WARNING] 4 | > This guide has been revised and moved. Find the updated instructions in [Integrate With Prometheus](https://kyma-project.io/#/telemetry-manager/user/integration/prometheus/README). 5 | 6 | ## Overview 7 | 8 | This example shows how to deploy and view alerting rules in Kyma. 9 | 10 | ## Prerequisites 11 | 12 | * Kyma as the target deployment environment. 13 | * Deployed custom kube-prometheus-stack as described in the [prometheus](../) example. 14 | 15 | ## Installation 16 | 17 | ### Add a new alerting rule 18 | 19 | 1. Create the PrometheusRule resource holding the configuration of your alerting rule. 20 | 21 | ```bash 22 | kubectl apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/monitoring-alert-rules/deployment/alert-rule.yaml 23 | ``` 24 | 25 | 2. Run the `port-forward` command on the `monitoring-prometheus` service to access the Prometheus dashboard. 26 | 27 | ```bash 28 | kubectl -n ${K8S_NAMESPACE} port-forward $(kubectl -n ${K8S_NAMESPACE} get service -l app=kube-prometheus-stack-prometheus -oname) 9090 29 | ``` 30 | 31 | 3. Go to `http://localhost:9090/rules` and find the **pod-not-running** rule. 32 | 33 | Because the `http-db-service` Deployment does not exist, Alertmanager fires an alert listed at `http://localhost:9090/alerts`. 34 | 35 | ### Stop the alert from getting fired 36 | 37 | 1. Export your Namespace as a variable: 38 | 39 | ```bash 40 | export K8S_NAMESPACE="{namespace}" 41 | ``` 42 | 43 | 2. To stop the alert from getting fired, create the Deployment: 44 | 45 | ```bash 46 | kubectl apply -f https://raw.githubusercontent.com/kyma-project/examples/main/http-db-service/deployment/deployment.yaml -n $K8S_NAMESPACE 47 | ``` 48 | 49 | ### Cleanup 50 | 51 | Run the following commands to completely remove the example and all its resources from the cluster: 52 | 53 | 1. Remove the **pod-not-running** alerting rule from the cluster: 54 | 55 | ```bash 56 | kubectl delete cm -n kyma-system -l example=monitoring-alert-rules 57 | ``` 58 | 59 | 2. Remove the **http-db-service** example and all its resources from the cluster: 60 | 61 | ```bash 62 | kubectl delete all -l example=http-db-service -n $K8S_NAMESPACE 63 | ``` 64 | -------------------------------------------------------------------------------- /orders-service/internal/service/order_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/kyma-project/examples/orders-service/internal/service/model" 10 | "github.com/kyma-project/examples/orders-service/internal/store" 11 | ) 12 | 13 | type Order struct { 14 | store store.Store 15 | } 16 | 17 | func NewOrders(store store.Store) *Order { 18 | return &Order{store: store} 19 | } 20 | 21 | func (o *Order) Get(ctx context.Context, id string) (*model.Order, error) { 22 | if id == "" { 23 | return nil, errors.New("id cannot be empty") 24 | } 25 | 26 | value, err := o.store.Get(ctx, fmt.Sprintf("order:%s", id)) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return o.toOrder(value) 32 | } 33 | 34 | func (o *Order) List(ctx context.Context) ([]model.Order, error) { 35 | keys, err := o.store.Keys(ctx, "order:*") 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | orders := make([]model.Order, 0, len(keys)) 41 | for _, key := range keys { 42 | value, err := o.store.Get(ctx, key) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | order, err := o.toOrder(value) 48 | if err != nil { 49 | return nil, err 50 | } 51 | orders = append(orders, *order) 52 | } 53 | 54 | return orders, nil 55 | } 56 | 57 | func (o *Order) Create(ctx context.Context, order *model.Order) error { 58 | if order == nil || order.Code == "" { 59 | return errors.New("invalid object") 60 | } 61 | 62 | bytes, err := json.Marshal(order) 63 | if err != nil { 64 | return errors.New("cannot marshal orders") 65 | } 66 | 67 | return o.store.Add(ctx, fmt.Sprintf("order:%s", order.Code), string(bytes)) 68 | } 69 | 70 | func (o *Order) DeleteAll(ctx context.Context) error { 71 | return o.store.Clear(ctx) 72 | } 73 | 74 | func (o *Order) DeleteOne(ctx context.Context, id string) error { 75 | return o.store.Delete(ctx, fmt.Sprintf("order:%s", id)) 76 | } 77 | 78 | func (o *Order) toOrder(value string) (*model.Order, error) { 79 | order := new(model.Order) 80 | err := json.Unmarshal([]byte(value), order) 81 | if err != nil { 82 | return nil, errors.New("cannot unmarshal order") 83 | } 84 | 85 | return order, nil 86 | } 87 | 88 | func (o *Order) GetStorageType(ctx context.Context) string { 89 | return o.store.GetStoreType(ctx) 90 | } 91 | -------------------------------------------------------------------------------- /http-db-service/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/kyma-project/examples/http-db-service/handler/events" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/gorilla/mux" 9 | "github.com/rs/cors" 10 | "github.com/vrischmann/envconfig" 11 | 12 | "github.com/kyma-project/examples/http-db-service/config" 13 | "github.com/kyma-project/examples/http-db-service/handler" 14 | "github.com/kyma-project/examples/http-db-service/internal/repository" 15 | 16 | _ "github.com/lib/pq" 17 | ) 18 | 19 | func main() { 20 | log.Println("Starting service...") 21 | 22 | var cfg config.Service 23 | if err := envconfig.Init(&cfg); err != nil { 24 | log.Panicf("Error loading main configuration %v\n", err.Error()) 25 | } 26 | log.Print(cfg) 27 | 28 | router := mux.NewRouter().StrictSlash(true) 29 | 30 | addOrderHandlers(router, cfg.DbType) 31 | addEventsHandler(router) 32 | addAPIHandler(router) 33 | 34 | if err := startService(cfg.Port, router); err != nil { 35 | log.Fatal("Unable to start server", err) 36 | } 37 | } 38 | 39 | func addOrderHandlers(router *mux.Router, dbType string) { 40 | 41 | repo, err := repository.Create(dbType) 42 | if err != nil { 43 | log.Fatal("Unable to initiate repository", err) 44 | } 45 | 46 | orderHandler := handler.NewOrderHandler(repo) 47 | 48 | // orders 49 | router.HandleFunc("/orders", orderHandler.InsertOrder).Methods(http.MethodPost) 50 | 51 | router.HandleFunc("/orders", orderHandler.GetOrders).Methods(http.MethodGet) 52 | router.HandleFunc("/namespace/{namespace}/orders", orderHandler.GetNamespaceOrders).Methods(http.MethodGet) 53 | 54 | router.HandleFunc("/orders", orderHandler.DeleteOrders).Methods(http.MethodDelete) 55 | router.HandleFunc("/namespace/{namespace}/orders", orderHandler.DeleteNamespaceOrders).Methods(http.MethodDelete) 56 | } 57 | 58 | func addEventsHandler(router *mux.Router) { 59 | router.HandleFunc("/events/order/created", events.HandleOrderCreatedEvent).Methods(http.MethodPost) 60 | 61 | } 62 | 63 | func addAPIHandler(router *mux.Router) { 64 | // API 65 | router.HandleFunc("/", handler.SwaggerAPIRedirectHandler).Methods(http.MethodGet) 66 | router.HandleFunc("/api.yaml", handler.SwaggerAPIHandler).Methods(http.MethodGet) 67 | } 68 | 69 | func startService(port string, router *mux.Router) error { 70 | log.Printf("Starting server on port %s ", port) 71 | 72 | c := cors.AllowAll() 73 | return http.ListenAndServe(":"+port, c.Handler(router)) 74 | } 75 | -------------------------------------------------------------------------------- /http-db-service/README.md: -------------------------------------------------------------------------------- 1 | # HTTP DB Service 2 | 3 | ## Overview 4 | 5 | This example demonstrates Kyma capabilities, such as HTTP endpoints that expose and bind a service to a database. The service in this example exposes HTTP endpoints used to create and read basic order JSON entities, as described in the [service's API descriptor](docs/api/api.yaml). The service can run with either an in-memory database or an MSSQL instance. By default, the in-memory database is enabled. The service in this example uses [Go](http://golang.org). 6 | 7 | ## Prerequisites 8 | 9 | - A [Docker](https://docs.docker.com/install) installation. 10 | - Kyma as the target deployment environment. 11 | - An MSSQL database for the service's database functionality. You can also use Azure MSSQL that you can provision using the Kyma Open Service Broker API. 12 | - [Golang](https://golang.org/dl/) and [dep](https://github.com/golang/dep) installed. 13 | 14 | ## Installation 15 | 16 | Use these commands to build and run the service with Docker: 17 | 18 | ``` 19 | ./build.sh 20 | docker run -it --rm -p 8017:8017 http-db-service:latest 21 | ``` 22 | 23 | To run the service with an MSSQL database, use the **DbType=mssql** environment variable in the application. To configure the connection to the database, set the environment variables for the values defined in the `config/db.go` file. 24 | 25 | The `deployment` folder contains `.yaml` descriptors used for the deployment of the service to Kyma. 26 | 27 | Run the following commands to deploy the published service to Kyma: 28 | 29 | 1. Export your Namespace as variable by replacing the `{namespace}` placeholder in the following command and running it: 30 | 31 | ```bash 32 | export K8S_NAMESPACE="{namespace}" 33 | ``` 34 | 2. Deploy the service: 35 | ```bash 36 | kubectl create namespace $K8S_NAMESPACE 37 | kubectl apply -f deployment/deployment.yaml -n $K8S_NAMESPACE 38 | ``` 39 | 40 | ### MSSQL Database tests 41 | 42 | To run the unit tests on a real MSSQL database, run the following command on the root of the example: 43 | 44 | ```bash 45 | docker run -ti -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password!123' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest 46 | ``` 47 | 48 | The command starts a MSSQL database in a container. 49 | 50 | ```bash 51 | username=sa password='Password!123' database=main tablename='test_orders' host=localhost port=1433 dbtype=mssql go test ./... -v 52 | ``` 53 | 54 | The command runs the specific unit tests for MSSQL databases with the environment information to connect to the previously started MSSQL database. 55 | 56 | ### Cleanup 57 | 58 | Run the following command to completely remove the example and all its resources from the cluster: 59 | 60 | ```bash 61 | kubectl delete all -l example=http-db-service -n $K8S_NAMESPACE 62 | ``` 63 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/mock_OrderRepository.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | package repository 3 | 4 | import mock "github.com/stretchr/testify/mock" 5 | 6 | // MockOrderRepository is an autogenerated mock type for the OrderRepository type 7 | type MockOrderRepository struct { 8 | mock.Mock 9 | } 10 | 11 | // cleanUp provides a mock function with given fields: 12 | func (_m *MockOrderRepository) cleanUp() error { 13 | ret := _m.Called() 14 | 15 | var r0 error 16 | if rf, ok := ret.Get(0).(func() error); ok { 17 | r0 = rf() 18 | } else { 19 | r0 = ret.Error(0) 20 | } 21 | 22 | return r0 23 | } 24 | 25 | // DeleteNamespaceOrders provides a mock function with given fields: ns 26 | func (_m *MockOrderRepository) DeleteNamespaceOrders(ns string) error { 27 | ret := _m.Called(ns) 28 | 29 | var r0 error 30 | if rf, ok := ret.Get(0).(func(string) error); ok { 31 | r0 = rf(ns) 32 | } else { 33 | r0 = ret.Error(0) 34 | } 35 | 36 | return r0 37 | } 38 | 39 | // DeleteOrders provides a mock function with given fields: 40 | func (_m *MockOrderRepository) DeleteOrders() error { 41 | ret := _m.Called() 42 | 43 | var r0 error 44 | if rf, ok := ret.Get(0).(func() error); ok { 45 | r0 = rf() 46 | } else { 47 | r0 = ret.Error(0) 48 | } 49 | 50 | return r0 51 | } 52 | 53 | // GetNamespaceOrders provides a mock function with given fields: ns 54 | func (_m *MockOrderRepository) GetNamespaceOrders(ns string) ([]Order, error) { 55 | ret := _m.Called(ns) 56 | 57 | var r0 []Order 58 | if rf, ok := ret.Get(0).(func(string) []Order); ok { 59 | r0 = rf(ns) 60 | } else { 61 | if ret.Get(0) != nil { 62 | r0 = ret.Get(0).([]Order) 63 | } 64 | } 65 | 66 | var r1 error 67 | if rf, ok := ret.Get(1).(func(string) error); ok { 68 | r1 = rf(ns) 69 | } else { 70 | r1 = ret.Error(1) 71 | } 72 | 73 | return r0, r1 74 | } 75 | 76 | // GetOrders provides a mock function with given fields: 77 | func (_m *MockOrderRepository) GetOrders() ([]Order, error) { 78 | ret := _m.Called() 79 | 80 | var r0 []Order 81 | if rf, ok := ret.Get(0).(func() []Order); ok { 82 | r0 = rf() 83 | } else { 84 | if ret.Get(0) != nil { 85 | r0 = ret.Get(0).([]Order) 86 | } 87 | } 88 | 89 | var r1 error 90 | if rf, ok := ret.Get(1).(func() error); ok { 91 | r1 = rf() 92 | } else { 93 | r1 = ret.Error(1) 94 | } 95 | 96 | return r0, r1 97 | } 98 | 99 | // InsertOrder provides a mock function with given fields: o 100 | func (_m *MockOrderRepository) InsertOrder(o Order) error { 101 | ret := _m.Called(o) 102 | 103 | var r0 error 104 | if rf, ok := ret.Get(0).(func(Order) error); ok { 105 | r0 = rf(o) 106 | } else { 107 | r0 = ret.Error(0) 108 | } 109 | 110 | return r0 111 | } 112 | -------------------------------------------------------------------------------- /service-binding/lambda/README.md: -------------------------------------------------------------------------------- 1 | # Bind a Service to a Lambda Function 2 | 3 | ## Overview 4 | 5 | This example shows how to bind lambda functions to a service. 6 | A lambda function connects to a Redis instance and retrieves statistics for this service. 7 | 8 | The example covers these tasks: 9 | 10 | 1. Create a Redis instance. 11 | 2. Create a function using Kubeless CLI. 12 | 3. Bind the function to the Redis Service. 13 | 4. Call the lambda function. 14 | 15 | ## Prerequisites 16 | 17 | - Install the Kubeless CLI as described in the [Kubeless installation guide](https://github.com/vmware-archive/kubeless/blob/master/docs/quick-start.md). 18 | 19 | 20 | ## Installation 21 | 22 | Apply a battery of `yaml` files to run the example. 23 | 24 | ### Steps 25 | 26 | 1. Export your Namespace as a variable by replacing the `{namespace}` placeholder in the following command and running it: 27 | ```bash 28 | export K8S_NAMESPACE="{namespace}" 29 | export KYMA_EXAMPLE_DOMAIN="{kyma domain}" 30 | ``` 31 | 32 | 2. Create a Redis instance: 33 | ```bash 34 | kubectl apply -f deployment/redis-instance.yaml -n $K8S_NAMESPACE 35 | ``` 36 | 37 | 3. Ensure that the Redis instance is provisioned: 38 | ```bash 39 | kubectl get serviceinstance/redis-instance -o jsonpath='{ .status.conditions[0].reason }' -n $K8S_NAMESPACE 40 | ``` 41 | 42 | 4. Create a Redis client through a lambda function, along with the ServiceBinding, and the ServiceBindingUsage custom resource: 43 | ```bash 44 | kubectl apply -f deployment/lambda-function.yaml -n $K8S_NAMESPACE 45 | ``` 46 | 47 | 5. Ensure that the Redis ServiceBinding works: 48 | ```bash 49 | kubectl get servicebinding/redis-instance-binding -o jsonpath='{ .status.conditions[0].reason }' -n $K8S_NAMESPACE 50 | ``` 51 | 52 | 6. Verify that the lambda function is ready: 53 | ```bash 54 | kubeless function ls redis-client -n $K8S_NAMESPACE 55 | ``` 56 | 57 | 7. Trigger the function. 58 | The information and statistics about the Redis server appear in the logs of the function Pod. 59 | ```bash 60 | curl -ik https://redis-client.$KYMA_EXAMPLE_DOMAIN 61 | ``` 62 | 63 | ### Cleanup 64 | 65 | Use this command to remove the example and all its resources from your Kyma cluster: 66 | 67 | ```bash 68 | kubectl delete all,api,function,servicebinding,serviceinstance,servicebindingusage -l example=service-binding -n $K8S_NAMESPACE 69 | ``` 70 | 71 | ## Troubleshooting 72 | 73 | Make sure the password is injected correctly into the Pod. The password should match the one in the [redis-instance.yaml](./deployment/redis-instance.yaml) 74 | 75 | ```bash 76 | kubectl exec -n $K8S_NAMESPACE -it $(kubectl get po -n $K8S_NAMESPACE -l example=service-binding-lambda --no-headers | awk '{print $1}') bash 77 | 78 | env | grep -i redis_password 79 | ``` 80 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/security-vulnerability.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security Vulnerability 3 | about: Report vulnerability in the project 4 | --- 5 | 6 | 11 | 12 | **Description** 13 | 14 | 16 | 17 | 18 | 19 | **Steps to exploit** 20 | 21 | 22 | 23 | **Risk assessment** 24 | 25 | 27 | [{CVSS Vector String}]({CVSS Calculator URL}) **{CVSS Base Score} ({CVSS Base Severity})** 28 | 51 | 52 | 53 | 54 | 55 | **Proposed mitigation** 56 | 57 | 58 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/README.md: -------------------------------------------------------------------------------- 1 | # Expose Custom Metrics in Kyma 2 | 3 | > [!WARNING] 4 | > This guide has been revised and moved. Find the updated instructions in [Integrate With Prometheus](https://kyma-project.io/#/telemetry-manager/user/integration/prometheus/README). 5 | 6 | 7 | ## Overview 8 | 9 | This example shows how to expose custom metrics to Prometheus with a Golang service in Kyma. To do so, follow these steps: 10 | 11 | 1. Expose a sample application serving metrics on the `8081` port. 12 | 2. Access the exposed metrics in Prometheus. 13 | 14 | ## Prerequisites 15 | 16 | - Kyma as the target deployment environment. 17 | - Deployed custom kube-prometheus-stack as described in the [prometheus](../) example. 18 | 19 | ## Installation 20 | 21 | ### Expose a sample metrics application 22 | 23 | 1. Export your Namespace as a variable. Replace the `{namespace}` placeholder in the following command and run it: 24 | 25 | ```bash 26 | export K8S_NAMESPACE="{namespace}" 27 | ``` 28 | 29 | 2. Ensure that your Namespace has Istio sidecar injection enabled. This example assumes that the metrics are exposed in a strict mTLS mode: 30 | 31 | ```bash 32 | kubectl label namespace ${K8S_NAMESPACE} istio-injection=enabled 33 | ``` 34 | 35 | 3. Deploy the service: 36 | 37 | ```bash 38 | kubectl apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/monitoring-custom-metrics/deployment/deployment.yaml -n $K8S_NAMESPACE 39 | ``` 40 | 41 | 4. Deploy the ServiceMonitor: 42 | 43 | ```bash 44 | kubectl apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/monitoring-custom-metrics/deployment/service-monitor.yaml 45 | ``` 46 | 47 | ### Access the exposed metrics in Prometheus 48 | 49 | 1. Run the `port-forward` command on the `monitoring-prometheus` service: 50 | 51 | ```bash 52 | kubectl -n ${K8S_NAMESPACE} port-forward $(kubectl -n ${K8S_NAMESPACE} get service -l app=kube-prometheus-stack-prometheus -oname) 9090 53 | ``` 54 | 55 | All the **sample-metrics** endpoints appear as `Targets` under `http://localhost:9090/targets#job-sample-metrics` list. 56 | 57 | 2. Use either `cpu_temperature_celsius` or `hd_errors_total` in the **expression** field under `http://localhost:9090/graph`. 58 | 3. Click the **Execute** button to check the values scraped by Prometheus. 59 | 60 | ### Cleanup 61 | 62 | Run the following commands to completely remove the example and all its resources from the cluster: 63 | 64 | 1. Remove ServiceMonitor in the `kyma-system` Namespace. 65 | 66 | ```bash 67 | kubectl delete servicemonitor -l example=monitoring-custom-metrics -n kyma-system 68 | ``` 69 | 70 | 2. Run the following command to completely remove the example service and all its resources from the cluster: 71 | 72 | ```bash 73 | kubectl delete all -l example=monitoring-custom-metrics -n $K8S_NAMESPACE 74 | ``` 75 | -------------------------------------------------------------------------------- /http-db-service/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # docker image name. 4 | PACKAGE_NAME=http-db-service 5 | 6 | set -e 7 | set -u 8 | set -o pipefail 9 | 10 | # Reset in case getopts. 11 | OPTIND=1 12 | 13 | VERBOSE=true 14 | HELP=false 15 | APP_VERSION=latest 16 | 17 | declare DOCKER_SYSTEM_NAME 18 | declare IMAGE_NAME_EXAMPLE=${PACKAGE_NAME}:${APP_VERSION} 19 | 20 | GREEN='\033[0;32m' 21 | NC='\033[0m' # No Color 22 | 23 | function usage() { 24 | echo "Build your example/http-db-service docker image." 25 | echo "Usage:" 26 | echo " $0 [-h] [-q] [-t]" 27 | echo 28 | echo "Flags:" 29 | echo " -h help, print usage" 30 | echo " -q quiet, to turn off VERBOSE" 31 | echo " -t tag, tag the image e.g:registry-name/image-name:tag-version, default:http-db-service:latest" 32 | echo "Example:" 33 | echo " ./build.sh" 34 | echo " ./build.sh -t registry:5000/example/http-db-service:0.0.1" 35 | echo " ./build.sh -h" 36 | echo " ./build.sh -q" 37 | } 38 | 39 | function debug() { 40 | echo "Config:" 41 | echo "Flags:" 42 | echo "- verbose : ${VERBOSE}" 43 | echo "result:" 44 | echo "- EXAMPLE IMAGE_NAME : ${IMAGE_NAME_EXAMPLE}" 45 | echo "- DOCKER_SYSTEM_NAME : ${DOCKER_SYSTEM_NAME}" 46 | } 47 | 48 | 49 | function setParameter() { 50 | if [[ $# -ne 1 ]]; then 51 | echo "No parameters are passed to getParameter" 52 | exit 1 53 | else 54 | IMAGE_NAME_EXAMPLE=$1 55 | fi 56 | } 57 | 58 | function onExit { 59 | local EXIT_CODE=$? 60 | if [[ ${VERBOSE} == "true" ]] && [[ ${HELP} == "false" ]]; then 61 | echo -e "${GREEN}Debug on exit${NC}" 62 | debug 63 | echo "exit code: ${EXIT_CODE}" 64 | fi 65 | } 66 | trap onExit EXIT 67 | 68 | 69 | OPTIND=1 70 | while getopts "ht:q" opt 71 | do 72 | case ${opt} in 73 | h) 74 | HELP=true 75 | usage 76 | exit 0 77 | ;; 78 | q) 79 | set +e 80 | VERBOSE=false 81 | set -e 82 | ;; 83 | t) 84 | set +e 85 | setParameter "$OPTARG" 86 | set -e 87 | ;; 88 | \?) 89 | echo "Unknown option: -$OPTARG" >&2; 90 | exit 1 91 | ;; 92 | esac 93 | done 94 | shift $((OPTIND-1)) 95 | 96 | ## inspect configured docker env 97 | DOCKER_SYSTEM_NAME=$(docker info -f "{{ .Name }}") 98 | 99 | ## perform image build 100 | if [[ ${VERBOSE} == "true" ]]; then echo -e "${GREEN}Build Example binary locally${NC}"; fi 101 | 102 | if [[ ${VERBOSE} == "true" ]]; then 103 | echo -e "${GREEN}Build docker image ${IMAGE_NAME_EXAMPLE} ${NC}" 104 | docker build -t "${IMAGE_NAME_EXAMPLE}" -f Dockerfile . 105 | echo -e "${GREEN}Built Docker image successfully...${NC}" 106 | else 107 | docker build -t "${IMAGE_NAME_EXAMPLE}" -f Dockerfile . > /dev/null 108 | fi 109 | 110 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/db_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "database/sql" 8 | 9 | _ "github.com/lib/pq" 10 | "github.com/stretchr/testify/assert" 11 | dbPkg "github.com/kyma-project/examples/http-db-service/internal/mssqldb" 12 | ) 13 | 14 | var newOrder = Order{OrderId: "orderId1", Namespace: "N7", Total: 10} 15 | 16 | const ( 17 | parsedInsert = "INSERT INTO tableName (order_id, namespace, total) VALUES (?, ?, ?)" 18 | parsedGet = "SELECT * FROM tableName" 19 | parsedDelete = "DELETE FROM tableName" 20 | ) 21 | 22 | func TestDbCreateSuccess(t *testing.T) { 23 | databaseMock := mockDbQuerier{} 24 | repo := orderRepositorySQL{&databaseMock, "tableName"} 25 | 26 | databaseMock.On("Exec", parsedInsert, newOrder.OrderId, newOrder.Namespace, newOrder.Total).Return((sql.Result)(nil), nil) 27 | //when 28 | err := repo.InsertOrder(newOrder) 29 | //then 30 | assert.Nil(t, err) 31 | } 32 | 33 | type primaryKeyViolationError struct{error} 34 | func (e primaryKeyViolationError) sqlErrorNumber() int32 { 35 | return dbPkg.PrimaryKeyViolation 36 | } 37 | 38 | func TestDbCreateDuplicate(t *testing.T) { 39 | databaseMock := mockDbQuerier{} 40 | repo := orderRepositorySQL{&databaseMock, "tableName"} 41 | 42 | databaseMock.On("Exec", parsedInsert, newOrder.OrderId, newOrder.Namespace, newOrder.Total). 43 | Return((sql.Result)(nil), primaryKeyViolationError{}) 44 | //when 45 | err := repo.InsertOrder(newOrder) 46 | //then 47 | assert.EqualValues(t, ErrDuplicateKey, err) 48 | } 49 | 50 | type otherSQLError struct{error} 51 | func (e otherSQLError) SQLErrorNumber() int32 { 52 | return 2 53 | } 54 | func TestDbRepositoryCreateOtherSqlError(t *testing.T) { 55 | databaseMock := mockDbQuerier{} 56 | repo := orderRepositorySQL{&databaseMock, "tableName"} 57 | 58 | databaseMock.On("Exec", parsedInsert, newOrder.OrderId, newOrder.Namespace, newOrder.Total). 59 | Return((sql.Result)(nil), otherSQLError{}) 60 | //when 61 | err := repo.InsertOrder(newOrder) 62 | //then 63 | assert.NotEqual(t, ErrDuplicateKey, err) 64 | } 65 | 66 | func TestDbCreateError(t *testing.T) { 67 | databaseMock := mockDbQuerier{} 68 | repo := orderRepositorySQL{&databaseMock, "tableName"} 69 | 70 | databaseMock.On("Exec", parsedInsert, newOrder.OrderId, newOrder.Namespace, newOrder.Total).Return((sql.Result)(nil), errors.New("unexpected error")) 71 | //when 72 | err := repo.InsertOrder(newOrder) 73 | //then 74 | assert.EqualError(t, err, "while inserting order: unexpected error") 75 | } 76 | 77 | func TestDbGetError(t *testing.T) { 78 | databaseMock := mockDbQuerier{} 79 | repo := orderRepositorySQL{&databaseMock, "tableName"} 80 | 81 | databaseMock.On("Query", parsedGet).Return(&sql.Rows{}, errors.New("unexpected error")) 82 | //when 83 | _, err := repo.GetOrders() 84 | //then 85 | assert.Error(t, err) 86 | } 87 | 88 | func TestDeleteOrders(t *testing.T) { 89 | databaseMock := mockDbQuerier{} 90 | repo := orderRepositorySQL{&databaseMock, "tableName"} 91 | databaseMock.On("Exec", parsedDelete).Return((sql.Result)(nil), nil) 92 | 93 | //when 94 | err := repo.DeleteOrders() 95 | 96 | //then 97 | assert.NoError(t, err) 98 | } 99 | -------------------------------------------------------------------------------- /service-binding/service/README.md: -------------------------------------------------------------------------------- 1 | # Bind a service to another service 2 | 3 | ## Overview 4 | 5 | This example shows how to bind service to a provided service in Kyma. 6 | The service exposes an HTTP API to perform database transactions and is bound to a provided service giving access to a MSSQL database in Azure. 7 | 8 | The example covers the following tasks: 9 | 10 | 1. Creating a MSSQL service instance from the Azure Broker. 11 | 2. Creating a service binding and binding usage to consume the service instance. 12 | 3. Deploying a microservice that performs transactions on the MSSQL database through an HTTP API. 13 | 14 | ## Prerequisites 15 | 16 | - Kyma as the target deployment environment. 17 | - [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) CLI tool to deploy the example's resources to Kyma. 18 | 19 | 20 | ## Installation 21 | 22 | Run the following commands to provision a managed MSSQL database, create a binding to it and deploy the service that will use the binding. 23 | 24 | 1. Export your Namespace as variable by replacing the `` placeholder in the following command and running it: 25 | ```bash 26 | export K8S_NAMESPACE="" 27 | ``` 28 | 29 | 2. Create a MSSQL instance. 30 | ```bash 31 | kubectl apply -f deployment/mssql-instance.yaml -n $K8S_NAMESPACE 32 | ``` 33 | 34 | 3. Ensure that the MSSQL instance is provisioned and running. 35 | ```bash 36 | kubectl get serviceinstance/mssql-instance -o jsonpath='{ .status.conditions[0].reason }' -n $K8S_NAMESPACE 37 | ``` 38 | > NOTE: Service instances usually take some minutes to be provisioned. 39 | 40 | 4. Deploy the service binding, the service binding usage and the http-db-service. 41 | ```bash 42 | kubectl apply -f deployment/mssql-binding-usage.yaml -n $K8S_NAMESPACE 43 | ``` 44 | 45 | 5. Ensure that the MSSQL service binding is provisioned. 46 | ```bash 47 | kubectl get ServiceBinding/mssql-instance-binding -o jsonpath='{ .status.conditions[0].reason }' -n $K8S_NAMESPACE 48 | ``` 49 | 6. Verify that the http-db-service is ready. 50 | ```bash 51 | kubectl get pods -l example=service-binding -n $K8S_NAMESPACE 52 | ``` 53 | 54 | 7. Forward the service port to be able to reach the service. 55 | ```bash 56 | kubectl port-forward -n $K8S_NAMESPACE $(kubectl get pod -n $K8S_NAMESPACE -l example=service-binding | grep http-db-service | awk '{print $1}') 8017 57 | ``` 58 | 8. Check that the service works as expected. 59 | 60 | Create an order: 61 | ```bash 62 | curl -d '{"orderId":"66", "total":9000}' -H "Content-Type: application/json" -X POST http://localhost:8017/orders 63 | ``` 64 | Check the created order 65 | ```bash 66 | curl http://localhost:8017/orders 67 | ``` 68 | 69 | ### Cleanup 70 | 71 | Run the following command to completely remove the example and all its resources from the cluster: 72 | 73 | ```bash 74 | kubectl delete all,sbu,servicebinding,serviceinstance -l example=service-binding-service -n $K8S_NAMESPACE 75 | ``` 76 | 77 | ## Troubleshooting 78 | 79 | ### Azure broker not available in minikube 80 | 81 | Currently this example does not have support for minikube and can't be run locally. 82 | -------------------------------------------------------------------------------- /prometheus/README-old.md: -------------------------------------------------------------------------------- 1 | # Monitoring in Kyma using a custom kube-prometheus-stack 2 | 3 | > [!WARNING] 4 | > This guide has been revised and moved. Find the updated instructions in [Integrate With Prometheus](https://kyma-project.io/#/telemetry-manager/user/integration/prometheus/README). 5 | 6 | ## Overview 7 | 8 | The Kyma Telemetry module provides collection and integration into observability backends based on OpenTelemetry. OpenTelemetry is the new vendor-neutral player in the cloud-native observability domain and has a growing adoption. However, it still lacks many features and sometimes the typical `kube-prometheus-stack` based on Prometheus, Grafana, and the surrounding helper tools is more appropriate. 9 | 10 | The following instructions describe the complete monitoring flow for your service running in Kyma. You get the gist of monitoring applications, such as Prometheus, Grafana, and Alertmanager. You learn how and where you can observe and visualize your service metrics to monitor them for any alerting values. 11 | 12 | All the tutorials use the [`monitoring-custom-metrics`](./monitoring-custom-metrics/README.md) example and one of its services called `sample-metrics`. This service exposes the `cpu_temperature_celsius` custom metric on the `/metrics` endpoint. This custom metric is the central element of the whole tutorial set. The metric value simulates the current processor temperature and changes randomly from 60 to 90 degrees Celsius. The alerting threshold in these tutorials is 75 degrees Celsius. If the temperature exceeds this value, the Grafana dashboard, PrometheusRule, and Alertmanager notifications you create inform you about this. 13 | 14 | ## Sequence of tasks 15 | 16 | The instructions cover the following tasks: 17 | 18 | ![Monitoring tutorials](./assets/monitoring-tutorials.svg) 19 | 20 | 1. [**Deploy a custom Prometheus stack**](./prometheus.md), in which you deploy the [kube-prometheus-stack](https://github.com/prometheus-operator/kube-prometheus) from the upstream Helm chart. 21 | 22 | 2. [**Observe application metrics**](./monitoring-custom-metrics/README.md), in which you redirect the `cpu_temperature_celsius` metric to the localhost and the Prometheus UI. You later observe how the metric value changes in the predefined 10-second interval in which Prometheus scrapes the metric values from the service's `/metrics` endpoint. 23 | 24 | 3. [**Create a Grafana dashboard**](./monitoring-grafana-dashboard/README.md), in which you create a Grafana dashboard of a Gauge type for the `cpu_temperature_celsius` metric. This dashboard shows explicitly when the CPU temperature is equal to or higher than the predefined threshold of 75 degrees Celsius, at which point the dashboard turns red. 25 | 26 | 4. [**Define alerting rules**](./monitoring-alert-rules/README.md), in which you define the `CPUTempHigh` alerting rule by creating a PrometheusRule resource. Prometheus accesses the `/metrics` endpoint every 10 seconds and validates the current value of the `cpu_temperature_celsius` metric. If the value is equal to or higher than 75 degrees Celsius, Prometheus waits for 10 seconds to recheck it. If the value still exceeds the threshold, Prometheus triggers the rule. You can observe both the rule and the alert it generates on the Prometheus dashboard. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![REUSE status](https://api.reuse.software/badge/github.com/kyma-project/examples)](https://api.reuse.software/info/github.com/kyma-project/examples) 2 | 3 | # Examples 4 | 5 | ## :warning: **Archived** 6 | 7 | This repository has been deprecated and archived. Replacements can be found in the individual [module pages](https://kyma-project.io/#/06-modules/README). Telemetry-related guides moved to the new [Telemetry integration guides](https://kyma-project.io/#/telemetry-manager/user/README?id=integration-guides). 8 | 9 | 10 | ## Overview 11 | 12 | The examples project provides a central repository to showcase and illustrate features and concepts on Kyma. 13 | 14 | > **NOTE:** The examples in this repository are provided as is, which means they may not always be up to date with the latest Kyma version. For current examples and tutorials, check our [documentation](https://kyma-project.io) page. 15 | 16 | ### What an example is 17 | 18 | - An example is a small demo that illustrates a particular feature or concept of Kyma. 19 | - An example refers to full, ready-to-use application development requiring no explanation. 20 | - An example must be concise and clear. 21 | 22 | ### What an example is not 23 | 24 | - An example cannot be a lecture or tutorial that guides the user through the topic with steps. Tutorials are part of the product documentation. 25 | - An example is not a production-ready application or service. Do not use examples in a production environment. 26 | 27 | ### List of examples 28 | 29 | The summary of the documentation in the `examples` repository lists all available examples organized by the feature or concept they showcase. This structure provides a quick overview and easy navigation. 30 | 31 | | Example | Description | Technology | 32 | |---|---|---| 33 | | [Orders Service](orders-service/README.md) | Test the service and function that expose HTTP APIs to collect simple orders either in the in-memory storage or the Redis database. | Go, NodeJS, Redis | 34 | | [HTTP DB Service](http-db-service/README.md) | Test the service that exposes an HTTP API to access a database on the cluster. | Go, MSSQL | 35 | | [Gateway](gateway/README.md) | Expose APIs for functions or services. | Kubeless | 36 | | [Service Binding](service-binding/lambda/README.md) | Bind a Redis service to a lambda function. | Kubeless, Redis, NodeJS | 37 | | [Event Email Service](event-email-service/README.md) | Send an automated email upon receiving an Event. | NodeJS | 38 | | [Kiali](kiali/README.md) | Deploy Kiali to Kyma. | Kiali, Istio, Prometheus | 39 | | [Prometheus](prometheus/README.md) | Deploy a Prometheus stack to monitor custom metrics. | Prometheus, Grafana | 40 | 41 | ## Installation 42 | 43 | You can run all the examples locally or in a cluster. For instructions on installing and running an example, refer to the `README.md` document of the specific example, either by using the **List of examples** section or by navigating through the project structure. 44 | 45 | ## Contributing 46 | 47 | 48 | See the [CONTRIBUTING](CONTRIBUTING.md) file. 49 | 50 | ## Code of Conduct 51 | 52 | 53 | See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) 54 | 55 | ## Licensing 56 | 57 | 58 | See the [LICENSE](LICENSE) file. 59 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | digest = "1:ac2a05be7167c495fe8aaf8aaf62ecf81e78d2180ecb04e16778dc6c185c96a5" 6 | name = "github.com/beorn7/perks" 7 | packages = ["quantile"] 8 | pruneopts = "" 9 | revision = "37c8de3658fcb183f997c4e13e8337516ab753e6" 10 | version = "v1.0.1" 11 | 12 | [[projects]] 13 | digest = "1:b852d2b62be24e445fcdbad9ce3015b44c207815d631230dfce3f14e7803f5bf" 14 | name = "github.com/golang/protobuf" 15 | packages = ["proto"] 16 | pruneopts = "" 17 | revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7" 18 | version = "v1.3.2" 19 | 20 | [[projects]] 21 | digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" 22 | name = "github.com/matttproud/golang_protobuf_extensions" 23 | packages = ["pbutil"] 24 | pruneopts = "" 25 | revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" 26 | version = "v1.0.1" 27 | 28 | [[projects]] 29 | digest = "1:c826496cad27bd9a7644a01230a79d472b4093dd33587236e8f8369bb1d8534e" 30 | name = "github.com/prometheus/client_golang" 31 | packages = [ 32 | "prometheus", 33 | "prometheus/internal", 34 | "prometheus/promhttp", 35 | ] 36 | pruneopts = "" 37 | revision = "2641b987480bca71fb39738eb8c8b0d577cb1d76" 38 | version = "v0.9.4" 39 | 40 | [[projects]] 41 | branch = "master" 42 | digest = "1:ff7a5f44653e65cf1a0577bbe3f2cdaf514930348f6df581bbd687bbe35ead5b" 43 | name = "github.com/prometheus/client_model" 44 | packages = ["go"] 45 | pruneopts = "" 46 | revision = "d1d2010b5beead3fa1c5f271a5cf626e40b3ad6e" 47 | 48 | [[projects]] 49 | digest = "1:8904acfa3ef080005c1fc0670ed0471739d1e211be5638cfa6af536b701942ae" 50 | name = "github.com/prometheus/common" 51 | packages = [ 52 | "expfmt", 53 | "internal/bitbucket.org/ww/goautoneg", 54 | "model", 55 | ] 56 | pruneopts = "" 57 | revision = "287d3e634a1e550c9e463dd7e5a75a422c614505" 58 | version = "v0.7.0" 59 | 60 | [[projects]] 61 | digest = "1:4c64aa254bc24990bc0216de9dd955ff83f061e9baac7ed2ffc293442ab7514a" 62 | name = "github.com/prometheus/procfs" 63 | packages = [ 64 | ".", 65 | "internal/fs", 66 | "internal/util", 67 | ] 68 | pruneopts = "" 69 | revision = "6d489fc7f1d9cd890a250f3ea3431b1744b9623f" 70 | version = "v0.0.8" 71 | 72 | [[projects]] 73 | branch = "master" 74 | digest = "1:465c831311f144be27d64ae3cb416b243ff67e73407852bc4265d09efbb66283" 75 | name = "golang.org/x/lint" 76 | packages = [ 77 | ".", 78 | "golint", 79 | ] 80 | pruneopts = "" 81 | revision = "fdd1cda4f05fd1fd86124f0ef9ce31a0b72c8448" 82 | 83 | [[projects]] 84 | branch = "master" 85 | digest = "1:8b1f1d0df0e98476b51f1b0e53094d8441dc71d5f00a5d7c486f42735a59ade3" 86 | name = "golang.org/x/tools" 87 | packages = [ 88 | "go/ast/astutil", 89 | "go/gcexportdata", 90 | "go/internal/gcimporter", 91 | "go/types/typeutil", 92 | ] 93 | pruneopts = "" 94 | revision = "7093a17b0467e77fb41804b2754a44f54a8c9ca2" 95 | 96 | [solve-meta] 97 | analyzer-name = "dep" 98 | analyzer-version = 1 99 | input-imports = [ 100 | "github.com/prometheus/client_golang/prometheus", 101 | "github.com/prometheus/client_golang/prometheus/promhttp", 102 | "golang.org/x/lint/golint", 103 | ] 104 | solver-name = "gps-cdcl" 105 | solver-version = 1 106 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/repo_integration_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "testing" 5 | "os" 6 | 7 | _ "github.com/lib/pq" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestRepositoryMemoryIntegration(t *testing.T) { 13 | runTestsForRepoType("memory", t) 14 | } 15 | 16 | func TestRepositoryDbIntegration(t *testing.T) { 17 | if os.Getenv("Host") == "" { 18 | t.Skip("skipping test; DB Config not set") 19 | } 20 | runTestsForRepoType("mssql", t) 21 | } 22 | 23 | func runTestsForRepoType(repositoryType string, t *testing.T) { 24 | repo, err := Create(repositoryType) 25 | require.NoError(t, err) 26 | 27 | newOrder := Order{OrderId: "orderId1", Namespace: "N7", Total: 10} 28 | 29 | t.Run("Create and get Order", func(t *testing.T) { 30 | //when 31 | err := repo.InsertOrder(newOrder) 32 | assert.NoError(t, err) 33 | 34 | //then 35 | resultOrders, err := repo.GetOrders() 36 | require.NoError(t, err) 37 | assert.Len(t, resultOrders, 1) 38 | assert.Equal(t, resultOrders[0].OrderId, "orderId1") 39 | assert.Equal(t, resultOrders[0].Namespace, "N7") 40 | assert.Equal(t, resultOrders[0].Total, float64(10)) 41 | 42 | resultOrders, err = repo.GetNamespaceOrders("N7") 43 | require.NoError(t, err) 44 | assert.Len(t, resultOrders, 1) 45 | assert.Equal(t, resultOrders[0].OrderId, "orderId1") 46 | assert.Equal(t, resultOrders[0].Namespace, "N7") 47 | assert.Equal(t, resultOrders[0].Total, float64(10)) 48 | }) 49 | 50 | t.Run("Return error when order already exists", func(t *testing.T) { 51 | //when 52 | err := repo.InsertOrder(newOrder) 53 | 54 | //then 55 | assert.Equal(t, ErrDuplicateKey, err) 56 | }) 57 | 58 | t.Run("Create orders in different namespaces", func(t *testing.T) { 59 | //when 60 | o1 := Order{OrderId: "orderId1", Namespace: "N8", Total: 10} 61 | o2 := Order{OrderId: "orderId1", Namespace: "N9", Total: 10} 62 | err := repo.InsertOrder(o1) 63 | assert.NoError(t, err) 64 | err = repo.InsertOrder(o2) 65 | assert.NoError(t, err) 66 | 67 | //then 68 | resultOrders, err := repo.GetNamespaceOrders("N8") 69 | require.NoError(t, err) 70 | assert.Len(t, resultOrders, 1) 71 | assert.Equal(t, resultOrders[0].OrderId, "orderId1") 72 | assert.Equal(t, resultOrders[0].Namespace, "N8") 73 | assert.Equal(t, resultOrders[0].Total, float64(10)) 74 | 75 | 76 | resultOrders, err = repo.GetNamespaceOrders("N9") 77 | require.NoError(t, err) 78 | assert.Len(t, resultOrders, 1) 79 | assert.Equal(t, resultOrders[0].OrderId, "orderId1") 80 | assert.Equal(t, resultOrders[0].Namespace, "N9") 81 | assert.Equal(t, resultOrders[0].Total, float64(10)) 82 | }) 83 | 84 | t.Run("Delete Namespace Orders", func(t *testing.T) { 85 | //when 86 | err := repo.DeleteNamespaceOrders("N7") 87 | assert.NoError(t, err) 88 | 89 | // no orders in N7 90 | resultOrders, err := repo.GetNamespaceOrders("N7") 91 | assert.NoError(t, err) 92 | assert.Len(t, resultOrders, 0) 93 | 94 | // all other orders still there 95 | resultOrders, err = repo.GetOrders() 96 | assert.NoError(t, err) 97 | assert.Len(t, resultOrders, 2) 98 | }) 99 | 100 | t.Run("Delete Orders", func(t *testing.T) { 101 | //when 102 | err := repo.DeleteOrders() 103 | assert.NoError(t, err) 104 | 105 | resultOrders, err := repo.GetOrders() 106 | assert.NoError(t, err) 107 | assert.Len(t, resultOrders, 0) 108 | }) 109 | 110 | repo.cleanUp() 111 | } -------------------------------------------------------------------------------- /orders-service/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "strings" 11 | "time" 12 | 13 | "github.com/go-redis/redis/v8" 14 | "github.com/gorilla/mux" 15 | "github.com/rs/cors" 16 | 17 | "github.com/kyma-project/examples/orders-service/internal/handler" 18 | "github.com/kyma-project/examples/orders-service/internal/service" 19 | "github.com/kyma-project/examples/orders-service/internal/store" 20 | ) 21 | 22 | const ( 23 | timeout = 15 * time.Second 24 | ) 25 | 26 | func main() { 27 | storage := createStorage() 28 | orderSvc := service.NewOrders(storage) 29 | 30 | r := mux.NewRouter() 31 | r.Use(logRequest) 32 | 33 | order := handler.NewOrder(orderSvc) 34 | order.RegisterAll("/orders", r) 35 | 36 | webhook := handler.NewWebhook(orderSvc) 37 | webhook.RegisterAll("/", r) 38 | 39 | log.Println("List of registered endpoints:") 40 | err := printEndpoints(r) 41 | if err != nil { 42 | log.Fatalf("Cannot print registered routes, because: %v", err) 43 | } 44 | 45 | port := os.Getenv("APP_PORT") 46 | if port == "" { 47 | port = "8080" 48 | } 49 | 50 | srv := http.Server{ 51 | Addr: fmt.Sprintf(":%s", port), 52 | Handler: cors.AllowAll().Handler(r), 53 | ReadTimeout: timeout, 54 | WriteTimeout: timeout, 55 | } 56 | 57 | go func() { 58 | if err := srv.ListenAndServe(); err != nil { 59 | log.Println(err) 60 | } 61 | }() 62 | 63 | log.Println(fmt.Sprintf("Listening on %s", srv.Addr)) 64 | onShutdown(srv, timeout) 65 | } 66 | 67 | func createStorage() store.Store { 68 | storage := createRedisStorage() 69 | if storage != nil { 70 | return storage 71 | } 72 | return store.NewMemory() 73 | } 74 | 75 | func createRedisStorage() store.Store { 76 | redisPrefix := os.Getenv("APP_REDIS_PREFIX") 77 | if redisPrefix == "" { 78 | redisPrefix = "REDIS_" 79 | } 80 | 81 | host := os.Getenv(fmt.Sprintf("%sHOST", redisPrefix)) 82 | port := os.Getenv(fmt.Sprintf("%sPORT", redisPrefix)) 83 | password := os.Getenv(fmt.Sprintf("%sREDIS_PASSWORD", redisPrefix)) 84 | 85 | if host != "" && port != "" && password != "" { 86 | redisClient := redis.NewClient(&redis.Options{ 87 | Addr: fmt.Sprintf("%s:%s", host, port), 88 | Password: password, 89 | }) 90 | log.Println(redisClient) 91 | return store.NewRedis(redisClient) 92 | } 93 | return nil 94 | } 95 | 96 | func logRequest(next http.Handler) http.Handler { 97 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 98 | log.Println(fmt.Sprintf("[%s] %s", r.Method, r.RequestURI)) 99 | next.ServeHTTP(w, r) 100 | }) 101 | } 102 | 103 | func printEndpoints(router *mux.Router) error { 104 | return router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { 105 | pathTemplate, err := route.GetPathTemplate() 106 | if err != nil { 107 | return err 108 | } 109 | 110 | methods, err := route.GetMethods() 111 | if err != nil { 112 | return err 113 | } 114 | 115 | log.Println(fmt.Sprintf("Path: %s, Methods: %s", pathTemplate, strings.Join(methods, ","))) 116 | return nil 117 | }) 118 | } 119 | 120 | func onShutdown(srv http.Server, timeout time.Duration) { 121 | c := make(chan os.Signal, 1) 122 | signal.Notify(c, os.Interrupt) 123 | <-c 124 | 125 | ctx, cancel := context.WithTimeout(context.Background(), timeout) 126 | defer cancel() 127 | srv.Shutdown(ctx) 128 | log.Println("Shutting down") 129 | os.Exit(0) 130 | } 131 | -------------------------------------------------------------------------------- /http-db-service/docs/api/api.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Order service and Events API 4 | version: "0.0.1" 5 | paths: 6 | /orders: 7 | post: 8 | description: Creates a new order. 9 | tags: 10 | - orders 11 | requestBody: 12 | content: 13 | application/json: 14 | schema: 15 | $ref: '#/components/schemas/Order' 16 | responses: 17 | '201': 18 | description: Order created succesfully. 19 | '400': 20 | description: Bad request. 21 | '409': 22 | description: Order ID conflict. 23 | '500': 24 | description: Internal server error. 25 | get: 26 | description: Retrieve all orders. 27 | tags: 28 | - orders 29 | responses: 30 | '200': 31 | description: Orders retrieved succesfully. 32 | content: 33 | application/json: 34 | schema: 35 | $ref: '#/components/schemas/OrderList' 36 | '500': 37 | description: Internal server error. 38 | delete: 39 | description: Delete all orders. 40 | tags: 41 | - orders 42 | responses: 43 | '204': 44 | description: All orders deleted succesfully. 45 | '500': 46 | description: Internal server error. 47 | /namespace/X/orders: 48 | get: 49 | description: Retrieve all orders. 50 | tags: 51 | - namespace orders 52 | responses: 53 | '200': 54 | description: Orders retrieved succesfully. 55 | content: 56 | application/json: 57 | schema: 58 | $ref: '#/components/schemas/OrderList' 59 | '400': 60 | description: Bad request. 61 | '500': 62 | description: Internal server error. 63 | delete: 64 | description: Delete all orders in namespace X. 65 | tags: 66 | - namespace orders 67 | responses: 68 | '204': 69 | description: All orders in namespace X deleted succesfully. 70 | '400': 71 | description: Bad request. 72 | '500': 73 | description: Internal server error. 74 | /events/order/created: 75 | post: 76 | description: Handle order created event 77 | tags: 78 | - events orders 79 | requestBody: 80 | content: 81 | application/json: 82 | schema: 83 | $ref: '#/components/schemas/OrderCreatedEvent' 84 | responses: 85 | '200': 86 | description: Order created event handled successfully. 87 | '400': 88 | description: Bad request. 89 | '500': 90 | description: Internal Server error. 91 | components: 92 | schemas: 93 | Order: 94 | type: object 95 | properties: 96 | orderId: 97 | type: string 98 | example: 11854638GU110615ELIN54ZQ 99 | namespace: 100 | type: string 101 | example: kyma-components 102 | total: 103 | type: number 104 | example: 1234.56 105 | required: 106 | - orderId 107 | - total 108 | OrderList: 109 | type: array 110 | items: 111 | $ref: '#/components/schemas/Order' 112 | OrderCreatedEvent: 113 | type: object 114 | properties: 115 | orderCode: 116 | type: string 117 | example: 76272727 118 | required: 119 | - orderCode -------------------------------------------------------------------------------- /prometheus/monitoring-grafana-dashboard/README.md: -------------------------------------------------------------------------------- 1 | # Create a Grafana dashboard 2 | 3 | > [!WARNING] 4 | > This guide has been revised and moved. Find the updated instructions in [Integrate With Prometheus](https://kyma-project.io/#/telemetry-manager/user/integration/prometheus/README). 5 | 6 | Follow these sections to create the Gauge dashboard type for the `cpu_temperature_celsius` metric. 7 | 8 | ## Prerequisites 9 | 10 | * Kyma as the target deployment environment. 11 | * Deployed custom kube-prometheus-stack as described in the [prometheus](../) example. 12 | * Deployed [custom-metrics example](../monitoring-custom-metrics). 13 | 14 | ## Create the dashboard 15 | 16 | 1. [Access Grafana](../README.md#verify-the-installation). 17 | 2. Add a new dashboard with a new panel. 18 | 3. For your new query, select **Prometheus** from the data source selector. 19 | 4. Pick the `cpu_temperature_celsius` metric. 20 | 5. To retrieve the latest metric value on demand, activate the **Instant** switch. 21 | 6. From the visualization panels, select the **Gauge** dashboard type. 22 | 7. Save your changes and provide a name for the dashboard. 23 | 24 | ## Configure the dashboard 25 | 26 | 1. To edit the dashboard settings, go to the **Panel Title** options and select **Edit**. 27 | 2. Find the **Field** tab and set the measuring unit to Celsius degrees, indicating the metric data type. 28 | 3. Set the minimum metric value to `60` and the maximum value to `90`, indicating the `cpu_temperature_celsius` metric value range. 29 | 4. For the dashboard to turn red once the CPU temperature reaches and exceeds 75°C, set a red color threshold to `75`. 30 | 5. Go to the **Panel** tab and title the dashboard, for example, `CPU Temperature`. 31 | 6. To display this range on the dashboard, make sure that under **Panel > Display**, the threshold labels and threshold markers are activated. 32 | 7. Save your changes. We recommend that you add a note to describe the changes made. 33 | 34 | ## Check the dashboard 35 | 36 | Refresh the browser to see how the dashboard changes according to the current value of the `cpu_temperature_celsius` metric. 37 | 38 | - If the current metric value ranges from 60 to 74 degrees Celsius, it turns **green**. 39 | - If the current metric value ranges from 75 to 90 degrees Celsius, it turns **red**. 40 | 41 | ## Add the dashboard as Kubernetes resource 42 | 43 | When you create a dashboard to monitor one of your applications (Function, microservice,...), we recommend that you define the dashboard as a Kubernetes ConfigMap resource. In this case, a Grafana sidecar automatically loads the Dashboard on Grafana startup. Following that approach, you can easily keep the dashboard definition together with the Kubernetes resource definitions of your application and port it to different clusters. 44 | 45 | 1. Create a JSON document with the dashboard definition; for example, by exporting it from Grafana. 46 | 2. Create a Kubernetes resource with a unique name for your dashboard and the JSON content, like the following example: 47 | 48 | ```yaml 49 | apiVersion: v1 50 | kind: ConfigMap 51 | metadata: 52 | name: {UNIQUE_DASHBOARD_NAME}-grafana-dashboard 53 | labels: 54 | grafana_dashboard: "1" 55 | data: 56 | {UNIQUE_DASHBOARD_NAME}-dashboard.json: |- 57 | { 58 | # dashboard JSON content 59 | } 60 | ``` 61 | 62 | 3. To apply the Kubernetes resource created in the previous step to your cluster, run: 63 | 64 | ```bash 65 | kubectl apply -f <{UNIQUE_CONFIGMAP_NAME}.yaml> 66 | ``` 67 | 68 | 4. Restart the Grafana deployment with the following command: 69 | 70 | ```bash 71 | kubectl -n ${K8S_NAMESPACE} rollout restart deployment ${HELM_RELEASE}-grafana 72 | ``` 73 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/memory_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "testing" 5 | 6 | _ "github.com/lib/pq" 7 | "github.com/stretchr/testify/require" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestMemoryCreateAndGet(t *testing.T) { 12 | repo := NewOrderRepositoryMemory() 13 | 14 | //when 15 | newOrder := Order{OrderId: "orderId1", Namespace: "N7", Total: 10} 16 | 17 | err := repo.InsertOrder(newOrder) 18 | 19 | if err != nil { 20 | t.Fatalf("Could not access database. '%s'", err) 21 | } 22 | 23 | resultOrders, err := repo.GetOrders() 24 | 25 | //then 26 | require.NoError(t, err) 27 | assert.Len(t, resultOrders, 1) 28 | assert.Equal(t, resultOrders[0].OrderId, "orderId1") 29 | assert.Equal(t, resultOrders[0].Total, float64(10)) 30 | } 31 | 32 | func TestMemoryErrorOnCreateDuplicate(t *testing.T) { 33 | Repo := NewOrderRepositoryMemory() 34 | 35 | //when 36 | newOrder := Order{OrderId: "orderId1", Namespace: "N7", Total: 10} 37 | 38 | err := Repo.InsertOrder(newOrder) 39 | err = Repo.InsertOrder(newOrder) 40 | 41 | //then 42 | assert.Equal(t, err, ErrDuplicateKey) 43 | } 44 | func TestMemoryGetByNamespaceSuccess(t *testing.T) { 45 | repo := NewOrderRepositoryMemory() 46 | 47 | //when 48 | orderN7 := Order{OrderId: "orderId1", Namespace: "N7", Total: 10} 49 | orderN8 := Order{OrderId: "orderId1", Namespace: "N8", Total: 10} 50 | 51 | err := repo.InsertOrder(orderN7) 52 | assert.NoError(t, err) 53 | err = repo.InsertOrder(orderN8) 54 | assert.NoError(t, err) 55 | 56 | // in total 2 orders 57 | resultOrders, err := repo.GetOrders() 58 | assert.NoError(t, err) 59 | assert.Len(t, resultOrders, 2) 60 | 61 | // 1 in N7 62 | resultOrders, err = repo.GetNamespaceOrders("N7") 63 | assert.NoError(t, err) 64 | assert.Len(t, resultOrders, 1) 65 | 66 | // 1 in N8 67 | resultOrders, err = repo.GetNamespaceOrders("N8") 68 | assert.NoError(t, err) 69 | assert.Len(t, resultOrders, 1) 70 | } 71 | 72 | func TestMemoryDeleteSuccess(t *testing.T) { 73 | repo := NewOrderRepositoryMemory() 74 | 75 | // ensure there is an order to delete 76 | err := repo.InsertOrder(Order{OrderId: "orderId1", Namespace: "N7", Total: 10}) 77 | assert.NoError(t, err) 78 | 79 | resultOrders, err := repo.GetOrders() 80 | assert.NoError(t, err) 81 | assert.Len(t, resultOrders, 1) 82 | 83 | // delete order and ensure it is gone 84 | err = repo.DeleteOrders() 85 | assert.NoError(t, err) 86 | 87 | resultOrders, err = repo.GetOrders() 88 | assert.NoError(t, err) 89 | assert.Len(t, resultOrders, 0) 90 | } 91 | 92 | func TestMemoryDeleteByNamespaceSuccess(t *testing.T) { 93 | repo := NewOrderRepositoryMemory() 94 | 95 | //when 96 | orderN7 := Order{OrderId: "orderId1", Namespace: "N7", Total: 10} 97 | orderN8 := Order{OrderId: "orderId1", Namespace: "N8", Total: 10} 98 | 99 | err := repo.InsertOrder(orderN7) 100 | err = repo.InsertOrder(orderN8) 101 | 102 | // in total 2 orders 103 | resultOrders, err := repo.GetOrders() 104 | assert.NoError(t, err) 105 | assert.Len(t, resultOrders, 2) 106 | 107 | // delete in N7 108 | err = repo.DeleteNamespaceOrders("N7") 109 | assert.NoError(t, err) 110 | 111 | // No orders in N7 112 | resultOrders, err = repo.GetNamespaceOrders("N7") 113 | assert.NoError(t, err) 114 | assert.Len(t, resultOrders, 0) 115 | 116 | // but still there in N8 117 | resultOrders, err = repo.GetNamespaceOrders("N8") 118 | assert.NoError(t, err) 119 | assert.Len(t, resultOrders, 1) 120 | 121 | // delete in N8 122 | err = repo.DeleteNamespaceOrders("N8") 123 | assert.NoError(t, err) 124 | 125 | // No orders in N8 126 | resultOrders, err = repo.GetNamespaceOrders("N7") 127 | assert.NoError(t, err) 128 | assert.Len(t, resultOrders, 0) 129 | 130 | // No orders at all 131 | resultOrders, err = repo.GetOrders() 132 | assert.NoError(t, err) 133 | assert.Len(t, resultOrders, 0) 134 | } 135 | -------------------------------------------------------------------------------- /prometheus/monitoring-custom-metrics/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math" 8 | "math/rand" 9 | "net/http" 10 | "time" 11 | 12 | "github.com/prometheus/client_golang/prometheus" 13 | "github.com/prometheus/client_golang/prometheus/promhttp" 14 | "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 15 | "go.opentelemetry.io/otel" 16 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace" 17 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" 18 | "go.opentelemetry.io/otel/sdk/resource" 19 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 20 | semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 21 | ) 22 | 23 | var ( 24 | cpuTemp = prometheus.NewGaugeFunc(prometheus.GaugeOpts{ 25 | Name: "cpu_temperature_celsius", 26 | Help: "Current temperature of the CPU.", 27 | }, randomTemp) 28 | hdFailures = prometheus.NewCounterVec( 29 | prometheus.CounterOpts{ 30 | Name: "hd_errors_total", 31 | Help: "Number of hard-disk errors.", 32 | }, 33 | []string{"device"}, 34 | ) 35 | cpuEnergy = prometheus.NewHistogramVec( 36 | prometheus.HistogramOpts{ 37 | Name: "cpu_energy_watt", 38 | Help: "Current power usage reported by the CPU.", 39 | Buckets: prometheus.LinearBuckets(0, 20, 5), 40 | }, 41 | []string{"core"}) 42 | hwHumidity = prometheus.NewSummaryVec( 43 | prometheus.SummaryOpts{ 44 | Name: "hw_humidity", 45 | Help: "The summary of humidity rate reported by a humidity sensor, as a ratio.", 46 | Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, 47 | }, 48 | []string{"sensor"}) 49 | tracer = otel.Tracer("github.com/kyma-project/examples/prometheus/monitoring-custom-metrics") 50 | ) 51 | 52 | func init() { 53 | // Metrics have to be registered to be exposed: 54 | prometheus.MustRegister(cpuTemp) 55 | prometheus.MustRegister(hdFailures) 56 | prometheus.MustRegister(cpuEnergy) 57 | prometheus.MustRegister(hwHumidity) 58 | } 59 | 60 | // randomTemp generates the temperature ranging from 60 to 90 61 | func randomTemp() float64 { 62 | return math.Round(rand.Float64()*300)/10 + 60 63 | } 64 | 65 | // randomEnergy generates the energy ranging from 0 to 100 66 | func randomEnergy() float64 { 67 | return math.Round(rand.Float64() * 100) 68 | } 69 | 70 | // randomHumidity generates the humidity from 0 to 1 71 | func randomHumidity() float64 { 72 | return rand.Float64() 73 | } 74 | 75 | func newTraceProvider(exp sdktrace.SpanExporter) *sdktrace.TracerProvider { 76 | // Ensure default SDK resources and the required service name are set. 77 | r, err := resource.Merge( 78 | resource.Default(), 79 | resource.NewWithAttributes( 80 | semconv.SchemaURL, 81 | semconv.ServiceName("monitoring-custom-metrics"), 82 | ), 83 | ) 84 | 85 | if err != nil { 86 | panic(err) 87 | } 88 | 89 | return sdktrace.NewTracerProvider( 90 | sdktrace.WithBatcher(exp), 91 | sdktrace.WithResource(r), 92 | ) 93 | } 94 | 95 | func main() { 96 | client := otlptracehttp.NewClient() 97 | ctx := context.Background() 98 | exporter, err := otlptrace.New(ctx, client) 99 | if err != nil { 100 | panic(fmt.Errorf("creating OTLP trace exporter: %w", err)) 101 | } 102 | // Create a new tracer provider with a batch span processor and the given exporter. 103 | tp := newTraceProvider(exporter) 104 | otel.SetTracerProvider(tp) 105 | 106 | // Handle shutdown properly so nothing leaks. 107 | defer func() { _ = tp.Shutdown(ctx) }() 108 | 109 | hdFailures.With(prometheus.Labels{"device": "/dev/sda"}).Inc() 110 | cpuEnergy.With(prometheus.Labels{"core": "0"}).Observe(randomEnergy()) 111 | hwHumidity.With(prometheus.Labels{"sensor": "0"}).Observe(randomHumidity()) 112 | 113 | // The Handler function provides a default handler to expose metrics 114 | handler := promhttp.Handler() 115 | 116 | // Wrap the handler with OpenTelemetry instrumentation 117 | wrappedHandler := otelhttp.NewHandler(handler, "/metrics") 118 | 119 | // via an HTTP server. "/metrics" is the usual endpoint for that. 120 | http.Handle("/metrics", wrappedHandler) 121 | 122 | http.DefaultClient.Timeout = 30 * time.Second 123 | 124 | //Port 8080 to be redirected to Envoy proxy 125 | log.Fatal(http.ListenAndServe(":8080", nil)) 126 | 127 | } 128 | -------------------------------------------------------------------------------- /orders-service/docs/openapi.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | info: 3 | title: Orders service API 4 | version: 0.1.0 5 | tags: 6 | - name: orders 7 | - name: webhook 8 | paths: 9 | /orders: 10 | get: 11 | description: Returns all orders from the system. 12 | tags: 13 | - orders 14 | responses: 15 | '200': 16 | description: Orders retrieved succesfully. 17 | content: 18 | application/json: 19 | schema: 20 | $ref: '#/components/schemas/OrderList' 21 | '500': 22 | description: Internal server error. 23 | post: 24 | description: Creates a new order in the system. Duplicates are not allowed. 25 | tags: 26 | - orders 27 | requestBody: 28 | content: 29 | application/json: 30 | schema: 31 | $ref: '#/components/schemas/Order' 32 | responses: 33 | '201': 34 | description: Order created succesfully. 35 | '400': 36 | description: Bad request. 37 | '409': 38 | description: Order ID conflict. 39 | '500': 40 | description: Internal server error. 41 | delete: 42 | description: Delete an orders collection. 43 | tags: 44 | - orders 45 | requestBody: 46 | content: 47 | application/json: 48 | schema: 49 | $ref: '#/components/schemas/Order' 50 | responses: 51 | '200': 52 | description: Collection cleared succesfully. 53 | '500': 54 | description: Internal server error. 55 | /orders/{id}: 56 | get: 57 | description: Returns an order with specified ID. 58 | tags: 59 | - orders 60 | parameters: 61 | - name: id 62 | in: path 63 | required: true 64 | description: ID of the order 65 | schema: 66 | type: string 67 | responses: 68 | '200': 69 | description: Orders retrieved succesfully. 70 | content: 71 | application/json: 72 | schema: 73 | $ref: '#/components/schemas/OrderList' 74 | '404': 75 | description: Order not found. 76 | '500': 77 | description: Internal server error. 78 | delete: 79 | description: Deletes an order with specified ID. 80 | tags: 81 | - orders 82 | parameters: 83 | - name: id 84 | in: path 85 | required: true 86 | description: ID of the order 87 | schema: 88 | type: string 89 | responses: 90 | '200': 91 | description: Order deleted succesfully. 92 | '404': 93 | description: Order not found. 94 | '500': 95 | description: Internal server error. 96 | /: 97 | post: 98 | description: Handle order event 99 | tags: 100 | - webhook 101 | requestBody: 102 | content: 103 | application/json: 104 | schema: 105 | $ref: '#/components/schemas/OrderEvent' 106 | responses: 107 | '200': 108 | description: Order event handled successfully. 109 | '400': 110 | description: Bad request. 111 | '409': 112 | description: Order ID conflict. 113 | '500': 114 | description: Internal Server error. 115 | components: 116 | schemas: 117 | Order: 118 | type: object 119 | properties: 120 | orderCode: 121 | type: string 122 | example: "123456789" 123 | description: ID of the order 124 | consignmentCode: 125 | type: string 126 | example: "123456789" 127 | description: ID of the order consignment code 128 | consignmentStatus: 129 | type: string 130 | example: "PICKUP_COMPLETE" 131 | description: Order consignment status 132 | required: 133 | - orderCode 134 | - consignmentCode 135 | - consignmentStatus 136 | OrderList: 137 | type: array 138 | items: 139 | $ref: '#/components/schemas/Order' 140 | OrderEvent: 141 | $ref: '#/components/schemas/Order' 142 | -------------------------------------------------------------------------------- /http-db-service/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc" 6 | name = "github.com/Sirupsen/logrus" 7 | packages = ["."] 8 | pruneopts = "UT" 9 | revision = "3e01752db0189b9157070a0e1668a620f9a85da2" 10 | version = "v1.0.6" 11 | 12 | [[projects]] 13 | digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" 14 | name = "github.com/davecgh/go-spew" 15 | packages = ["spew"] 16 | pruneopts = "UT" 17 | revision = "346938d642f2ec3594ed81d874461961cd0faa76" 18 | version = "v1.1.0" 19 | 20 | [[projects]] 21 | branch = "master" 22 | digest = "1:a452a32cb5d17cb1ceb4867bb7a8c8ec12fe68d08e29a60869e091c3b1c8ea02" 23 | name = "github.com/denisenkom/go-mssqldb" 24 | packages = [ 25 | ".", 26 | "internal/cp", 27 | ] 28 | pruneopts = "UT" 29 | revision = "270bc3860bb94dd3a3ffd047377d746c5e276726" 30 | 31 | [[projects]] 32 | digest = "1:d5f97fc268267ec1b61c3453058c738246fc3e746f14b1ae25161513b7367b0c" 33 | name = "github.com/gorilla/mux" 34 | packages = ["."] 35 | pruneopts = "UT" 36 | revision = "c5c6c98bc25355028a63748a498942a6398ccd22" 37 | version = "v1.7.1" 38 | 39 | [[projects]] 40 | digest = "1:bc1c0be40c67b6b4aee09d7508d5a2a52c1c116b1fa43806dad2b0d6b4d4003b" 41 | name = "github.com/lib/pq" 42 | packages = [ 43 | ".", 44 | "oid", 45 | "scram", 46 | ] 47 | pruneopts = "UT" 48 | revision = "51e2106eed1cea199c802d2a49e91e2491b02056" 49 | version = "v1.1.0" 50 | 51 | [[projects]] 52 | digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" 53 | name = "github.com/pkg/errors" 54 | packages = ["."] 55 | pruneopts = "UT" 56 | revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" 57 | version = "v0.8.1" 58 | 59 | [[projects]] 60 | digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" 61 | name = "github.com/pmezard/go-difflib" 62 | packages = ["difflib"] 63 | pruneopts = "UT" 64 | revision = "792786c7400a136282c1664665ae0a8db921c6c2" 65 | version = "v1.0.0" 66 | 67 | [[projects]] 68 | digest = "1:d2f8ec47e1957eaf4d4ea9ddfcb9b4c33771229104669e697ee07c9a9d54918e" 69 | name = "github.com/rs/cors" 70 | packages = ["."] 71 | pruneopts = "UT" 72 | revision = "feef513b9575b32f84bafa580aad89b011259019" 73 | version = "v1.3.0" 74 | 75 | [[projects]] 76 | digest = "1:1bc4a5bd879ce6b44fdaa2e8e1144921c145604764e92220009b951c1f2439f5" 77 | name = "github.com/stretchr/objx" 78 | packages = ["."] 79 | pruneopts = "UT" 80 | revision = "facf9a85c22f48d2f52f2380e4efce1768749a89" 81 | version = "v0.1" 82 | 83 | [[projects]] 84 | digest = "1:b762f96c1183763894e8dabbac5f8e8d5821754b6e2686a8c398a174b7a58ff5" 85 | name = "github.com/stretchr/testify" 86 | packages = [ 87 | "assert", 88 | "mock", 89 | "require", 90 | ] 91 | pruneopts = "UT" 92 | revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" 93 | version = "v1.3.0" 94 | 95 | [[projects]] 96 | digest = "1:1b8282c02f3cbf27de019d739246eaf8231f5dbcaaf9d0d7c7524184ee7b2d24" 97 | name = "github.com/vrischmann/envconfig" 98 | packages = ["."] 99 | pruneopts = "UT" 100 | revision = "7a443243d539c9595dd8aa7f03a6f68707b80e66" 101 | version = "v1.1.0" 102 | 103 | [[projects]] 104 | branch = "master" 105 | digest = "1:ee04668811bcca0958f730d2595813a8d2561e9a9e68b0387de6362545d9823b" 106 | name = "golang.org/x/crypto" 107 | packages = [ 108 | "md4", 109 | "ssh/terminal", 110 | ] 111 | pruneopts = "UT" 112 | revision = "b4956d363a8b14623cc81db401707679605930a3" 113 | 114 | [[projects]] 115 | branch = "master" 116 | digest = "1:3364d01296ce7eeca363e3d530ae63a2092d6f8efb85fb3d101e8f6d7de83452" 117 | name = "golang.org/x/sys" 118 | packages = [ 119 | "unix", 120 | "windows", 121 | ] 122 | pruneopts = "UT" 123 | revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4" 124 | 125 | [solve-meta] 126 | analyzer-name = "dep" 127 | analyzer-version = 1 128 | input-imports = [ 129 | "github.com/Sirupsen/logrus", 130 | "github.com/denisenkom/go-mssqldb", 131 | "github.com/gorilla/mux", 132 | "github.com/lib/pq", 133 | "github.com/pkg/errors", 134 | "github.com/rs/cors", 135 | "github.com/stretchr/testify/assert", 136 | "github.com/stretchr/testify/mock", 137 | "github.com/stretchr/testify/require", 138 | "github.com/vrischmann/envconfig", 139 | ] 140 | solver-name = "gps-cdcl" 141 | solver-version = 1 142 | -------------------------------------------------------------------------------- /orders-service/internal/handler/order_handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | 11 | "github.com/gorilla/mux" 12 | 13 | "github.com/kyma-project/examples/orders-service/internal/service" 14 | "github.com/kyma-project/examples/orders-service/internal/service/model" 15 | "github.com/kyma-project/examples/orders-service/internal/store" 16 | ) 17 | 18 | type Order struct { 19 | svc *service.Order 20 | } 21 | 22 | type Router interface { 23 | HandleFunc(path string, f func(http.ResponseWriter, *http.Request)) *mux.Route 24 | } 25 | 26 | func NewOrder(svc *service.Order) *Order { 27 | return &Order{ 28 | svc: svc, 29 | } 30 | } 31 | 32 | func (o *Order) RegisterAll(root string, router Router) { 33 | router.HandleFunc(fmt.Sprintf("%s", root), o.OnList).Methods(http.MethodGet) 34 | router.HandleFunc(fmt.Sprintf("%s/{id}", root), o.OnRead).Methods(http.MethodGet) 35 | router.HandleFunc(fmt.Sprintf("%s", root), o.OnCreate).Methods(http.MethodPost) 36 | router.HandleFunc(fmt.Sprintf("%s", root), o.OnDeleteAll).Methods(http.MethodDelete) 37 | router.HandleFunc(fmt.Sprintf("%s/{id}", root), o.OnDeleteOne).Methods(http.MethodDelete) 38 | router.HandleFunc("/storagetype", o.OnGetStorageType).Methods(http.MethodGet) 39 | } 40 | 41 | func (o *Order) OnList(w http.ResponseWriter, r *http.Request) { 42 | defer r.Body.Close() 43 | ctx, cancel := context.WithCancel(context.Background()) 44 | defer cancel() 45 | 46 | list, err := o.svc.List(ctx) 47 | if err != nil { 48 | log.Println(err) 49 | w.WriteHeader(http.StatusInternalServerError) 50 | return 51 | } 52 | 53 | js, err := json.Marshal(list) 54 | if err != nil { 55 | log.Println(err) 56 | http.Error(w, err.Error(), http.StatusInternalServerError) 57 | return 58 | } 59 | 60 | w.Header().Set("Content-Type", "application/json") 61 | w.Write(js) 62 | } 63 | 64 | func (o *Order) OnRead(w http.ResponseWriter, r *http.Request) { 65 | defer r.Body.Close() 66 | ctx, cancel := context.WithCancel(context.Background()) 67 | defer cancel() 68 | 69 | vars := mux.Vars(r) 70 | 71 | order, err := o.svc.Get(ctx, vars["id"]) 72 | if err == store.NotFoundError { 73 | log.Println(err) 74 | w.WriteHeader(http.StatusNotFound) 75 | return 76 | } else if err != nil { 77 | log.Println(err) 78 | w.WriteHeader(http.StatusInternalServerError) 79 | return 80 | } 81 | 82 | js, err := json.Marshal(order) 83 | if err != nil { 84 | log.Println(err) 85 | http.Error(w, err.Error(), http.StatusInternalServerError) 86 | return 87 | } 88 | 89 | w.Header().Set("Content-Type", "application/json") 90 | w.Write(js) 91 | } 92 | 93 | func (o *Order) OnCreate(w http.ResponseWriter, r *http.Request) { 94 | defer r.Body.Close() 95 | ctx, cancel := context.WithCancel(context.Background()) 96 | defer cancel() 97 | 98 | body, err := ioutil.ReadAll(r.Body) 99 | if err != nil { 100 | log.Println(err) 101 | w.WriteHeader(http.StatusBadRequest) 102 | return 103 | } 104 | 105 | order := new(model.Order) 106 | if err := json.Unmarshal(body, order); err != nil { 107 | log.Println(err) 108 | w.WriteHeader(http.StatusBadRequest) 109 | return 110 | } 111 | 112 | err = o.svc.Create(ctx, order) 113 | if err == store.AlreadyExistsError { 114 | w.WriteHeader(http.StatusConflict) 115 | return 116 | } else if err != nil { 117 | w.WriteHeader(http.StatusInternalServerError) 118 | return 119 | } 120 | 121 | w.WriteHeader(http.StatusCreated) 122 | } 123 | 124 | func (o *Order) OnDeleteAll(w http.ResponseWriter, r *http.Request) { 125 | defer r.Body.Close() 126 | ctx, cancel := context.WithCancel(context.Background()) 127 | defer cancel() 128 | 129 | err := o.svc.DeleteAll(ctx) 130 | if err != nil { 131 | log.Println(err) 132 | w.WriteHeader(http.StatusInternalServerError) 133 | return 134 | } 135 | 136 | w.WriteHeader(http.StatusOK) 137 | } 138 | 139 | func (o *Order) OnDeleteOne(w http.ResponseWriter, r *http.Request) { 140 | defer r.Body.Close() 141 | ctx, cancel := context.WithCancel(context.Background()) 142 | defer cancel() 143 | 144 | vars := mux.Vars(r) 145 | err := o.svc.DeleteOne(ctx, vars["id"]) 146 | if err == store.NotFoundError { 147 | log.Println(err) 148 | w.WriteHeader(http.StatusNotFound) 149 | return 150 | } else if err != nil { 151 | log.Println(err) 152 | w.WriteHeader(http.StatusInternalServerError) 153 | return 154 | } 155 | 156 | w.WriteHeader(http.StatusOK) 157 | } 158 | 159 | func (o *Order) OnGetStorageType(w http.ResponseWriter, r *http.Request) { 160 | defer r.Body.Close() 161 | ctx, cancel := context.WithCancel(context.Background()) 162 | defer cancel() 163 | 164 | storageType := o.svc.GetStorageType(ctx) 165 | 166 | w.Header().Set("Content-Type", "text/plain") 167 | w.Write([]byte(storageType)) 168 | } 169 | -------------------------------------------------------------------------------- /orders-service/deployment/orders-function.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serverless.kyma-project.io/v1alpha1 2 | kind: Function 3 | metadata: 4 | name: orders-function 5 | namespace: orders-service 6 | labels: 7 | app: orders-function 8 | example: orders-function 9 | spec: 10 | deps: |- 11 | { 12 | "name": "orders-function", 13 | "version": "1.0.0", 14 | "dependencies": { 15 | "redis": "3.0.2" 16 | } 17 | } 18 | maxReplicas: 1 19 | minReplicas: 1 20 | resources: 21 | limits: 22 | cpu: 20m 23 | memory: 32Mi 24 | requests: 25 | cpu: 10m 26 | memory: 16Mi 27 | env: 28 | - name: APP_REDIS_PREFIX 29 | value: "REDIS_" 30 | source: |- 31 | const redis = require("redis"); 32 | const { promisify } = require("util"); 33 | 34 | let storage = undefined; 35 | const errors = { 36 | codeRequired: new Error("orderCode is required"), 37 | alreadyExists: new Error("object already exists"), 38 | } 39 | 40 | module.exports = { 41 | main: async function (event, _) { 42 | const storage = getStorage(); 43 | 44 | if (!event.data || !Object.keys(event.data).length) { 45 | return await onList(storage, event); 46 | } 47 | 48 | const { orderCode, consignmentCode, consignmentStatus } = event.data; 49 | if (orderCode && consignmentCode && consignmentStatus) { 50 | return await onCreate(storage, event); 51 | } 52 | 53 | event.extensions.response.status(500); 54 | } 55 | } 56 | 57 | async function onList(storage, event) { 58 | try { 59 | return await storage.getAll(); 60 | } catch(err) { 61 | event.extensions.response.status(500); 62 | return; 63 | } 64 | } 65 | 66 | async function onCreate(storage, event) { 67 | try { 68 | await storage.set(event.data); 69 | } catch(err) { 70 | let status = 500; 71 | switch (err) { 72 | case errors.codeRequired: { 73 | status = 400; 74 | break; 75 | }; 76 | case errors.alreadyExists: { 77 | status = 409; 78 | break; 79 | }; 80 | } 81 | event.extensions.response.status(status); 82 | } 83 | } 84 | 85 | class RedisStorage { 86 | storage = undefined; 87 | asyncGet = void 0; 88 | asyncKeys = void 0; 89 | asyncSet = void 0; 90 | 91 | constructor(options) { 92 | this.storage = redis.createClient(options); 93 | this.asyncGet = promisify(this.storage.get).bind(this.storage); 94 | this.asyncKeys = promisify(this.storage.keys).bind(this.storage); 95 | this.asyncSet = promisify(this.storage.set).bind(this.storage); 96 | } 97 | 98 | async getAll() { 99 | let values = []; 100 | 101 | const keys = await this.asyncKeys("*"); 102 | for (const key of keys) { 103 | const value = await this.asyncGet(key); 104 | values.push(JSON.parse(value)); 105 | } 106 | 107 | return values; 108 | } 109 | 110 | async set(order = {}) { 111 | if (!order.orderCode) { 112 | throw errors.codeRequired; 113 | } 114 | const value = await this.asyncGet(order.orderCode); 115 | if (value) { 116 | throw errors.alreadyExists; 117 | } 118 | await this.asyncSet(order.orderCode, JSON.stringify(order)); 119 | } 120 | } 121 | 122 | class InMemoryStorage { 123 | storage = new Map(); 124 | 125 | getAll() { 126 | return Array.from(this.storage) 127 | .map(([_, order]) => order) 128 | } 129 | 130 | set(order = {}) { 131 | if (!order.orderCode) { 132 | throw errors.codeRequired; 133 | } 134 | if (this.storage.get(order.orderCode)) { 135 | throw errors.alreadyExists; 136 | } 137 | return this.storage.set(order.orderCode, order); 138 | } 139 | } 140 | 141 | function readEnv(env = "") { 142 | return process.env[env] || undefined; 143 | } 144 | 145 | function createStorage() { 146 | let redisPrefix = readEnv("APP_REDIS_PREFIX"); 147 | if (!redisPrefix) { 148 | redisPrefix = "REDIS_"; 149 | } 150 | const port = readEnv(redisPrefix + "PORT"); 151 | const host = readEnv(redisPrefix + "HOST"); 152 | const password = readEnv(redisPrefix + "REDIS_PASSWORD"); 153 | 154 | if (host && port && password) { 155 | return new RedisStorage({ host, port, password }); 156 | } 157 | return new InMemoryStorage(); 158 | } 159 | 160 | function getStorage() { 161 | if (!storage) { 162 | storage = createStorage(); 163 | } 164 | return storage; 165 | } 166 | -------------------------------------------------------------------------------- /http-db-service/internal/repository/db.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "io" 7 | log "github.com/Sirupsen/logrus" 8 | "github.com/vrischmann/envconfig" 9 | "github.com/pkg/errors" 10 | 11 | dbPkg "github.com/kyma-project/examples/http-db-service/internal/mssqldb" 12 | ) 13 | 14 | const ( 15 | insertQuery = "INSERT INTO %s (order_id, namespace, total) VALUES (?, ?, ?)" 16 | getQuery = "SELECT * FROM %s" 17 | getNSQuery = "SELECT * FROM %s WHERE namespace = ?" 18 | deleteQuery = "DELETE FROM %s" 19 | deleteNSQuery = "DELETE FROM %s WHERE namespace = ?" 20 | 21 | ) 22 | 23 | type orderRepositorySQL struct { 24 | database dbQuerier 25 | ordersTableName string 26 | } 27 | 28 | //go:generate mockery -name dbQuerier -inpkg 29 | type dbQuerier interface { 30 | Exec(query string, args ...interface{}) (sql.Result, error) 31 | Query(query string, args ...interface{}) (*sql.Rows, error) 32 | io.Closer 33 | } 34 | 35 | // NewOrderRepositoryDb is used to instantiate and return the DB implementation of the OrderRepository. 36 | // The connection to the database is created by initiating the configuration defined 37 | // in https://github.com/kyma-project/examples/blob/main/http-db-service/internal/mssqldb/config.go 38 | func NewOrderRepositoryDb() (OrderRepository, error) { 39 | var ( 40 | dbCfg dbPkg.Config 41 | database dbQuerier 42 | err error 43 | ) 44 | if err = envconfig.Init(&dbCfg); err != nil { 45 | return nil, errors.Wrap(err, "Error loading db configuration %v.") 46 | } 47 | if database, err = dbPkg.InitDb(dbCfg); err != nil { 48 | return nil, errors.Wrap(err, "Error loading db configuration %v.") 49 | } 50 | return &orderRepositorySQL{database, dbCfg.DbOrdersTableName}, nil 51 | } 52 | 53 | type sqlError interface { 54 | sqlErrorNumber() int32 55 | } 56 | 57 | func (repository *orderRepositorySQL) InsertOrder(order Order) error { 58 | q := fmt.Sprintf(insertQuery, dbPkg.SanitizeSQLArg(repository.ordersTableName)) 59 | log.Debugf("Running insert order query: '%q'.", q) 60 | _, err := repository.database.Exec(q, order.OrderId, order.Namespace, order.Total) 61 | 62 | if errorWithNumber, ok := err.(sqlError); ok { 63 | if errorWithNumber.sqlErrorNumber() == dbPkg.PrimaryKeyViolation { 64 | return ErrDuplicateKey 65 | } 66 | } 67 | 68 | return errors.Wrap(err, "while inserting order") 69 | } 70 | 71 | func (repository *orderRepositorySQL) GetOrders() ([]Order, error) { 72 | q := fmt.Sprintf(getQuery, dbPkg.SanitizeSQLArg(repository.ordersTableName)) 73 | log.Debugf("Quering orders: '%q'.", q) 74 | rows, err := repository.database.Query(q) 75 | 76 | if err != nil { 77 | return nil, errors.Wrap(err, "while reading orders from DB") 78 | } 79 | 80 | defer rows.Close() 81 | return readFromResult(rows) 82 | } 83 | 84 | func (repository *orderRepositorySQL) GetNamespaceOrders(ns string) ([]Order, error) { 85 | q := fmt.Sprintf(getNSQuery, dbPkg.SanitizeSQLArg(repository.ordersTableName)) 86 | log.Debugf("Quering orders for namespace: '%q'.", q) 87 | rows, err := repository.database.Query(q, ns) 88 | 89 | if err != nil { 90 | return nil, errors.Wrapf(err, "while reading orders for namespace: '%q' from DB", ns) 91 | } 92 | 93 | defer rows.Close() 94 | return readFromResult(rows) 95 | } 96 | 97 | func (repository *orderRepositorySQL) DeleteOrders() error { 98 | q := fmt.Sprintf(deleteQuery, dbPkg.SanitizeSQLArg(repository.ordersTableName)) 99 | log.Debugf("Deleting orders: '%q'.", q) 100 | _, err := repository.database.Exec(q) 101 | 102 | if err != nil { 103 | return errors.Wrap(err, "while deleting orders") 104 | } 105 | return nil 106 | } 107 | 108 | func (repository *orderRepositorySQL) DeleteNamespaceOrders(ns string) error { 109 | q := fmt.Sprintf(deleteNSQuery, dbPkg.SanitizeSQLArg(repository.ordersTableName)) 110 | log.Debugf("Deleting orders: '%q'.", q) 111 | _, err := repository.database.Exec(q, ns) 112 | 113 | if err != nil { 114 | return errors.Wrap(err, "while deleting orders") 115 | } 116 | return nil 117 | } 118 | 119 | func readFromResult(rows *sql.Rows) ([]Order, error) { 120 | orderList := make([]Order, 0) 121 | for rows.Next() { 122 | order := Order{} 123 | if err := rows.Scan(&order.OrderId, &order.Namespace, &order.Total); err != nil { 124 | return []Order{}, err 125 | } 126 | orderList = append(orderList, order) 127 | } 128 | return orderList, nil 129 | } 130 | 131 | func (repository *orderRepositorySQL) cleanUp() error { 132 | log.Debug("Removing DB table") 133 | 134 | if _, err := repository.database.Exec("DROP TABLE " + dbPkg.SanitizeSQLArg(repository.ordersTableName)); err != nil { 135 | return errors.Wrap(err, "while removing the DB table.") 136 | } 137 | if err := repository.database.Close(); err != nil { 138 | return errors.Wrap(err, "while closing connection to the DB.") 139 | } 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /http-db-service/handler/order.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/kyma-project/examples/http-db-service/handler/response" 7 | "io/ioutil" 8 | "net/http" 9 | 10 | log "github.com/Sirupsen/logrus" 11 | "github.com/gorilla/mux" 12 | 13 | "github.com/kyma-project/examples/http-db-service/internal/repository" 14 | ) 15 | 16 | const defaultNamespace = "default" 17 | 18 | // Order is used to expose the Order service's basic operations using the HTTP route handler methods which extend it. 19 | type Order struct { 20 | repository repository.OrderRepository 21 | } 22 | 23 | // NewOrderHandler creates a new 'OrderHandler' which provides route handlers for the given OrderRepository's operations. 24 | func NewOrderHandler(repository repository.OrderRepository) Order { 25 | return Order{repository} 26 | } 27 | 28 | // InsertOrder handles an http request for creating an Order given in JSON format. 29 | // The handler also validates the Order payload fields and handles duplicate entry or unexpected errors. 30 | func (orderHandler Order) InsertOrder(w http.ResponseWriter, r *http.Request) { 31 | b, err := ioutil.ReadAll(r.Body) 32 | if err != nil { 33 | log.Error("Error parsing request.", err) 34 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 35 | return 36 | } 37 | 38 | defer r.Body.Close() 39 | var order repository.Order 40 | err = json.Unmarshal(b, &order) 41 | if err != nil || order.OrderId == "" || order.Total == 0 { 42 | response.WriteCodeAndMessage(http.StatusBadRequest, "Invalid request body, orderId / total fields cannot be empty.", w) 43 | return 44 | } 45 | if order.Namespace == "" { 46 | order.Namespace = defaultNamespace 47 | } 48 | 49 | log.Debugf("Inserting order: '%+v'.", order) 50 | err = orderHandler.repository.InsertOrder(order) 51 | 52 | switch err { 53 | case nil: 54 | w.WriteHeader(http.StatusCreated) 55 | case repository.ErrDuplicateKey: 56 | response.WriteCodeAndMessage(http.StatusConflict, fmt.Sprintf("Order %s already exists.", order.OrderId), w) 57 | default: 58 | log.Error(fmt.Sprintf("Error inserting order: '%+v'", order), err) 59 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 60 | } 61 | } 62 | 63 | // GetOrders handles an http request for retrieving all Orders from all namespaces. 64 | // The orders list is marshalled in JSON format and sent to the `http.ResponseWriter` 65 | func (orderHandler Order) GetOrders(w http.ResponseWriter, r *http.Request) { 66 | log.Debug("Retrieving orders") 67 | 68 | orders, err := orderHandler.repository.GetOrders() 69 | if err != nil { 70 | log.Error("Error retrieving orders.", err) 71 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 72 | return 73 | } 74 | 75 | if err = respondOrders(orders, w); err != nil { 76 | log.Error("Error sending orders response.", err) 77 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 78 | return 79 | } 80 | } 81 | 82 | // GetNamespaceOrders handles an http request for retrieving all Orders from a namespace specified as a path variable. 83 | // The orders list is marshalled in JSON format and sent to the `http.ResponseWriter`. 84 | func (orderHandler Order) GetNamespaceOrders(w http.ResponseWriter, r *http.Request) { 85 | ns, exists := mux.Vars(r)["namespace"] 86 | if !exists { 87 | response.WriteCodeAndMessage(http.StatusBadRequest, "No namespace provided.", w) 88 | return 89 | } 90 | 91 | log.Debugf("Retrieving orders for namespace: %s\n", ns) 92 | 93 | orders, err := orderHandler.repository.GetNamespaceOrders(ns) 94 | if err != nil { 95 | log.Error("Error retrieving orders.", err) 96 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 97 | return 98 | } 99 | 100 | if err = respondOrders(orders, w); err != nil { 101 | log.Error("Error sending orders response.", err) 102 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 103 | return 104 | } 105 | } 106 | 107 | func respondOrders(orders []repository.Order, w http.ResponseWriter) error { 108 | body, err := json.Marshal(orders) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | w.Header().Set("Content-Type", "application/json;charset=UTF-8") 114 | w.WriteHeader(http.StatusOK) 115 | if _, err := w.Write(body); err != nil { 116 | return err 117 | } 118 | return nil 119 | } 120 | 121 | // DeleteOrders handles an http request for deleting all Orders from all namespaces. 122 | func (orderHandler Order) DeleteOrders(w http.ResponseWriter, r *http.Request) { 123 | log.Debug("Deleting all orders") 124 | 125 | if err := orderHandler.repository.DeleteOrders(); err != nil { 126 | log.Error("Error deleting orders.", err) 127 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 128 | return 129 | } 130 | w.WriteHeader(http.StatusNoContent) 131 | } 132 | 133 | // DeleteNamespaceOrders handles an http request for deleting all Orders from a namespace specified as a path variable. 134 | func (orderHandler Order) DeleteNamespaceOrders(w http.ResponseWriter, r *http.Request) { 135 | ns, exists := mux.Vars(r)["namespace"] 136 | if !exists { 137 | response.WriteCodeAndMessage(http.StatusBadRequest, "No namespace provided.", w) 138 | return 139 | } 140 | 141 | log.Debugf("Deleting orders in namespace %s\n", ns) 142 | if err := orderHandler.repository.DeleteNamespaceOrders(ns); err != nil { 143 | log.Errorf("Deleting orders in namespace %s\n. %s", ns, err) 144 | response.WriteCodeAndMessage(http.StatusInternalServerError, "Internal error.", w) 145 | return 146 | } 147 | w.WriteHeader(http.StatusNoContent) 148 | } 149 | -------------------------------------------------------------------------------- /kiali/README.md: -------------------------------------------------------------------------------- 1 | # Install custom Kiali in Kyma 2 | 3 | > [!WARNING] 4 | > This guide is archived and not maintained anymore. Current integration guides around observability are located [here](https://kyma-project.io/#/telemetry-manager/user/README?id=integration-guides). 5 | 6 | ## Overview 7 | 8 | The following instructions outline how to install [`Kiali`](https://github.com/kiali/helm-charts/tree/master/kiali-operator) in Kyma. 9 | 10 | ## Prerequisites 11 | 12 | - Kyma as the target deployment runtime 13 | - A [Prometheus instance preserving Istio metrics](./../prometheus/) deployed to the runtime. 14 | - kubectl version 1.26.x or higher 15 | - Helm 3.x 16 | 17 | ## Installation 18 | 19 | ### Preparation 20 | 21 | 1. Export your Namespace as a variable. Replace the `{namespace}` placeholder in the following command and run it: 22 | 23 | ```bash 24 | export K8S_NAMESPACE="{namespace}" 25 | ``` 26 | 27 | 1. Export the Helm release name that you want to use. The release name must be unique for the chosen Namespace. Be aware that all resources in the cluster will be prefixed with that name. Run the following command: 28 | ```bash 29 | export HELM_KIALI_RELEASE="kiali" 30 | ``` 31 | 32 | 1. Update your Helm installation with the required Helm repository: 33 | 34 | ```bash 35 | helm repo add kiali https://kiali.org/helm-charts 36 | helm repo update 37 | ``` 38 | 39 | ### Install the kiali-operator 40 | 41 | > **NOTE:** Kiali recommends to install Kiali always with the Kiali operator; that's why the following step uses the Kiali operator Helm chart. 42 | 43 | Run the Helm upgrade command, which installs the chart if not present yet. 44 | ```bash 45 | export PROM_SERVICE_NAME=$(kubectl -n ${K8S_NAMESPACE} get service -l app=kube-prometheus-stack-prometheus -ojsonpath='{.items[*].metadata.name}') 46 | helm upgrade --install --create-namespace -n $K8S_NAMESPACE $HELM_KIALI_RELEASE kiali/kiali-operator -f https://raw.githubusercontent.com/kyma-project/examples/main/kiali/values.yaml --set cr.spec.external_services.prometheus.url=http://$PROM_SERVICE_NAME.$K8S_NAMESPACE:9090 47 | ``` 48 | 49 | You can either use the [`values.yaml`](./values.yaml) provided in this `kiali` folder, which contains customized settings deviating from the default settings, or create your own `values.yaml` file. 50 | 51 | ### Verify the installation 52 | 53 | Check that the `kiali-operator` and `kiali-server` Pods have been created in the Namespace and are in the `Running` state: 54 | ```bash 55 | kubectl -n $K8S_NAMESPACE rollout status deploy $HELM_KIALI_RELEASE-kiali-operator && kubectl -n $K8S_NAMESPACE rollout status deploy kiali-server 56 | ``` 57 | ### Access Kiali 58 | 59 | To access Kiali, either use kubectl port forwarding, or expose it using the Kyma Ingress Gateway. 60 | 61 | * To access Kiali using port forwarding, run: 62 | ```bash 63 | kubectl -n $K8S_NAMESPACE port-forward svc/kiali-server 20001 64 | ``` 65 | 66 | Open Kiali in your browser under `http://localhost:20001` and log in with a [Kubernetes service account token](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#service-account-tokens), for instance, from your kubeconfig file. 67 | 68 | * To expose Kiali using the Kyma API Gateway, create an APIRule: 69 | ```bash 70 | kubectl -n $K8S_NAMESPACE apply -f https://raw.githubusercontent.com/kyma-project/examples/main/kiali/apirule.yaml 71 | ``` 72 | Get the public URL of your Kiali server: 73 | ```bash 74 | kubectl -n $K8S_NAMESPACE get vs -l apirule.gateway.kyma-project.io/v1beta1=kiali.$K8S_NAMESPACE -ojsonpath='{.items[*].spec.hosts[*]}' 75 | ``` 76 | 77 | ### Deploy a custom workload and invoke 78 | 79 | To see the service communication visualized in Kiali, follow the instructions in [`orders-service`](./../orders-service/). 80 | 81 | ## Advanced Topics 82 | 83 | ### Integrate Jaeger 84 | 85 | If you use [Jaeger](https://www.jaegertracing.io/) for distributed tracing, Kiali can use your Jaeger instance to [provide traces](https://kiali.io/docs/features/tracing/). 86 | 87 | For integration instructions, read [Kiali: Jaeger configuration](https://kiali.io/docs/configuration/p8s-jaeger-grafana/tracing/jaeger/). 88 | 89 | ### Integrate Grafana 90 | 91 | Kiali can provide links to Istio dashboards in Grafana. 92 | 93 | For integration instructions, read [Kiali: Grafana configuration](https://kiali.io/docs/configuration/p8s-jaeger-grafana/grafana/). 94 | 95 | ### Authentication 96 | 97 | Kiali supports different authentication strategies. The default authentication strategy uses a Kubernetes Service Account Token. If you use a kubeconfig file with a static token, you can use this token to authenticate. Depending on your preferred way to access Kiali, different authentication strategies might be suitable. To learn more about Kiali authentication strategies, read [Kiali: Authentication Strategies](https://kiali.io/docs/configuration/authentication/). 98 | 99 | * For Kiali access by port forwarding, you need no additional authentication, and you can activate the [anonymous strategy](https://kiali.io/docs/configuration/authentication/anonymous/): 100 | ```bash 101 | helm upgrade --install --create-namespace -n $K8S_NAMESPACE $HELM_KIALI_RELEASE kiali/kiali-operator --set cr.spec.auth.strategy=anonymous -f https://raw.githubusercontent.com/kyma-project/examples/main/kiali/values.yaml 102 | ``` 103 | * When exposing the Kiali server over the ingress gateway, we recommend to use an external identity provider compatible with OpenID Connect (OIDC). Find the required settings at [Kiali: OpenID Connect strategy](https://kiali.io/docs/configuration/authentication/openid/). 104 | 105 | ## Cleanup 106 | 107 | When you're done, you can remove the example and all its resources from the cluster. 108 | 109 | 1. Remove the stack by calling Helm: 110 | 111 | ```bash 112 | helm delete -n $K8S_NAMESPACE $HELM_KIALI_RELEASE 113 | kubectl -n $K8S_NAMESPACE delete -f https://raw.githubusercontent.com/kyma-project/examples/main/kiali/apirule.yaml 114 | ``` 115 | 116 | 2. If you created the `$K8S_NAMESPACE` Namespace specifically for this tutorial, remove the Namespace: 117 | ```bash 118 | kubectl delete namespace $K8S_NAMESPACE 119 | ``` 120 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | To contribute to this project, follow the rules from the general [CONTRIBUTING.md](https://github.com/kyma-project/community/blob/main/CONTRIBUTING.md) document in the `community` repository. 4 | 5 | For additional guidelines, see other sections of this document. 6 | 7 | ## Naming guidelines 8 | 9 | In addition to the general [naming guidelines](https://github.com/kyma-project/community/blob/main/docs/guidelines/technical-guidelines/01-naming.md), the `examples` repository has these conventions to follow: 10 | 11 | - Folders: 12 | - All folder names are lowercase and connected with a dash (`-`) when they are comprised of two or more words. For example, `http-db-service`. 13 | - Folder names are as short as possible while being clear and descriptive. 14 | 15 | - Files: 16 | - All markdown files are suffixed with the `.md` extension. Any `README.md` or `CONTRIBUTING.md` document names are in uppercase. 17 | - Source code files follow their own programming language naming conventions. 18 | - Configuration and Deployment files follow their own platform naming conventions, such as `Dockerfile` or `Gopkg.toml`. If there is no convention specified, filenames must have a valid extension, be lowercase, and be connected with a dash (`-`) when they are comprised of two or more words. 19 | 20 | ## Documentation types 21 | 22 | ### README.md 23 | 24 | This file type contains information about what an example illustrates, and the instructions on how to run it. Each example in this repository requires a `README.md` document. 25 | 26 | To create new `README.md` documents for new examples, use the [template](https://github.com/kyma-project/template-repository/blob/main/README.md) provided. 27 | Do not change the names or the order of the main sections in the `README.md` documents. However, you can create subsections to adjust each `README.md` document to the example's specific requirements. See the example of a [README.md](http-db-service/README.md) document. 28 | 29 | Find all `README.md` documents listed in the **List of examples** section in the main [README.md](README.md) document. When you create a new `README.md` document, add a new entry to the **List of examples** table. Follow these steps: 30 | 31 | * Go to the **List of examples** section in the main [README.md](README.md) document located in the root of the `examples` project. 32 | * Locate the new `README.md` document in the repository structure. 33 | * Add a new entry to the relevant place on the table. Each entry consists of these three columns: 34 | * A relative link to the corresponding `README.md` document. The alias is the name of the project that the `README.md` document describes. 35 | * A short description of the example. 36 | * A list of the technologies used. 37 | 38 | ## Contribution rules 39 | 40 | To contribute to the project, see the [CONTRIBUTING.md](https://github.com/kyma-project/community/blob/main/CONTRIBUTING.md) document in the `community` repository. 41 | 42 | In addition to these general rules, every contributor to the `examples` repository must follow the rules described in the subsections. 43 | 44 | ### Structure 45 | 46 | Structure examples according to the feature or concept they illustrate. Optionally, if a feature or concept has examples in several languages or technologies, add a second level of nesting to separate the examples. Do not use more than two levels of nesting as it hinders navigation and readability. 47 | 48 | To add a new example, follow this layout structure: 49 | 50 | ``` 51 | kubectl-container-describe/ # No subdivision 52 | serverless-lambda/ # Subdivision based on language 53 | |---js/ 54 | |---go/ 55 | broker-gateway/ # Subdivision based on provider 56 | |--azure/ 57 | |--aws/ 58 | |--gcp/ 59 | storage/ # Subdivision based on technology 60 | |--cassandra/ 61 | |--mysql 62 | |--rethinkdb 63 | ``` 64 | 65 | > **NOTE:** This is an example layout and does not reflect the actual structure. It is based on the [Kubernetes examples structure](https://github.com/kubernetes/examples). 66 | 67 | ### Build 68 | 69 | For all examples, the main `README.md` documents include the build scripts and the instructions on how to run them. Additionally, provide the instructions on how to clean up or delete the examples if applicable. 70 | 71 | ### Deploy 72 | 73 | All examples are deployable in the cloud. Follow these rules to achieve this and to keep the complexity to a minimum: 74 | 75 | - All examples have only one `Dockerfile` which generates a single Docker image. The Docker image follows the Docker image [naming conventions](https://github.com/kyma-project/community/blob/main/docs/guidelines/technical-guidelines/01-naming.md). 76 | - Only example images that are completely built are accessible in the registry, so the examples can be deployed to the cloud without the user having to build and push a new image. 77 | - All `README.md` documents in the examples provide the scripts to build and deploy images locally, and the instructions on how to run them. 78 | - Provide Deployment configurations and descriptors in a user-friendly format such as a `yaml` file, and never in bundled formats such as Helm charts, unless the example itself illustrates the usage of bundled formats. 79 | - Deployed resources of an example have a label defined in the `example: {Example Name}` format. 80 | - Deployed resources of an example do not specify a Namespace. This way, you can deploy to the Namespace of your choice. As an exception, you can include a Namespace in the example's Deployment if it is necessary to preserve the integrity of the cluster or the user's Namespace. For example, your example deploys a custom Event Bus that would crash the cluster's eventing if it were deployed into the default or user's Namespace. 81 | - Provide the instructions for each example on how to clean up all the deployed resources based on the label of the example. 82 | 83 | ### Continuous integration 84 | 85 | A CI/CD configuration file accompanies each example to ensure its validity as part of the automation pipeline. 86 | 87 | ### Releases 88 | 89 | Each example is independently releasable by creating a `tag` in the repository with the `{example-name}/{version}` format. This triggers a release for the given example from its CI/CD configuration. Push released example Docker images to the `example` folder in the Kyma repository, under `eu.gcr.io/kyma-project/example`. 90 | -------------------------------------------------------------------------------- /orders-service/README.md: -------------------------------------------------------------------------------- 1 | # Orders Service 2 | 3 | ## Overview 4 | 5 | This example demonstrates how you can use Kyma to expose microservices and Functions on HTTP endpoints and bind them to an external database. 6 | 7 | This example contains: 8 | 9 | - A sample application (microservice) written in [Go](http://golang.org). It can expose HTTP endpoints used to create, read, and delete basic order JSON entities, as described in the [service's OpenAPI specification](docs/openapi.yaml). This service can run with either a default in-memory database or the external Redis database. 10 | 11 | - A [serverless](https://kyma-project.io/#/01-overview/serverless/README) Function with the ability to expose HTTP endpoints used to read all order records or post single orders. Just like the microservice, the Function can run with either the default in-memory database or the external Redis instance. See the source code of this Function in the [`function.yaml`](./deployment/orders-function.yaml)) file under the **spec.source** field. 12 | 13 | To see this microservice and Function in action, see the [getting started guides](https://github.com/kyma-project/kyma/blob/release-1.24/docs/getting-started/01-overview.md) and learn more about exposing services and Functions through API Rule CRs. You will also learn how to bind them to an external application like Redis and subscribe them to events from a sample mock application. 14 | 15 | ## Prerequisites 16 | 17 | - Kyma 1.14 or higher. To deploy the Function, [Serverless](https://github.com/kyma-project/kyma/tree/release-1.24/docs/serverless) must be installed on the cluster 18 | - [Kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/) 1.16 or higher 19 | - [Helm](https://helm.sh/) 3.0 or higher (optional) 20 | 21 | ## Installation 22 | 23 | You can install Orders Service (microservice or Function) either through kubectl or Helm. 24 | 25 | ### Use kubectl 26 | 27 | To install the microservice on a Kyma cluster, run: 28 | 29 | ```bash 30 | kubectl create ns orders-service 31 | kubectl apply -f ./deployment/orders-service-deployment.yaml 32 | kubectl apply -f ./deployment/orders-service-service.yaml 33 | ``` 34 | 35 | To install the Function on a Kyma cluster, run: 36 | 37 | ```bash 38 | kubectl create ns orders-service 39 | kubectl apply -f ./deployment/orders-function.yaml 40 | ``` 41 | 42 | ### Use Helm 43 | 44 | To install the microservice on a Kyma cluster, run: 45 | 46 | ```bash 47 | helm install orders-service --namespace orders-service --create-namespace --timeout 60s --wait ./chart 48 | ``` 49 | 50 | See the [`values.yaml`](./chart/values.yaml) file for the configuration of the Helm release. 51 | 52 | ## Cleanup 53 | 54 | See how to remove the example from the cluster through kubectl and Helm. 55 | 56 | ### Use kubectl 57 | 58 | Run this command to completely remove the microservice and all its resources from the cluster: 59 | 60 | ```bash 61 | kubectl delete all -l app=orders-service -n orders-service 62 | kubectl delete ns orders-service 63 | ``` 64 | 65 | Run this command to completely remove the Function and all its resources from the cluster: 66 | 67 | ```bash 68 | kubectl delete all -l app=orders-function -n orders-service 69 | kubectl delete ns orders-service 70 | ``` 71 | 72 | ### Use Helm 73 | 74 | Run this command to completely remove the Helm release with the example and all its resources from the cluster: 75 | 76 | ```bash 77 | helm delete orders-service -n orders-service 78 | kubectl delete ns orders-service 79 | ``` 80 | 81 | ## Configuration 82 | 83 | To configure the microservice or the Function, override the default values of these environment variables: 84 | 85 | | Environment variable | Description | Required | Default value | 86 | | ---------------------- | ----------------------------------------------------------------------------- | ------ | ------------- | 87 | | **APP_PORT** | Specifies the port of the running service. The function doesn't use this variable. | No | `8080` | 88 | | **APP_REDIS_PREFIX** | Specifies the prefix for all Redis environment variables. See the variables in other rows. | No | `REDIS_` | 89 | | **{APP_REDIS_PREFIX}HOST** | Specifies the host of the Redis instance. | No | `nil` | 90 | | **{APP_REDIS_PREFIX}PORT** | Specifies the port of the Redis instance. | No | `nil` | 91 | | **{APP_REDIS_PREFIX}REDIS_PASSWORD** | Specifies the password to authorize access to the Redis instance. | No | `nil` | 92 | 93 | See the example: 94 | 95 | ```bash 96 | export APP_REDIS_PREFIX="R_" 97 | export R_HOST="abc.com" 98 | export R_PORT="8080" 99 | export R_REDIS_PASSWORD="xyz" 100 | ``` 101 | 102 | > **NOTE:** To allow the microservice and the Function to communicate with the Redis instance, you must provide the **{APP_REDIS_PREFIX}HOST**, **{APP_REDIS_PREFIX}PORT**, **{APP_REDIS_PREFIX}REDIS_PASSWORD** environments. Otherwise, the microservice and the Function will always use in-memory storage. 103 | 104 | ## Testing 105 | 106 | Learn how to test both the microservice and the Function. 107 | 108 | ### Microservice 109 | 110 | To send a sample order to the microservice, run: 111 | 112 | ```bash 113 | curl -X POST ${APP_URL}/orders -k \ 114 | -H "Content-Type: application/json" -d \ 115 | '{ 116 | "consignmentCode": "76272727", 117 | "orderCode": "76272725", 118 | "consignmentStatus": "PICKUP_COMPLETE" 119 | }' 120 | ``` 121 | 122 | To retrieve all orders saved in storage, run: 123 | 124 | ```bash 125 | curl -X GET ${APP_URL}/orders -k 126 | ``` 127 | 128 | **APP_URL** is the URL of the running microservice. See the [tutorial on exposing an application with an API Rule](https://github.com/kyma-project/kyma/blob/release-1.24/docs/api-gateway/08-02-exposesecure.md) for reference. 129 | 130 | 131 | > **TIP:** See the [service's OpenAPI specification](docs/openapi.yaml) for details of all endpoints. 132 | 133 | ### Function 134 | 135 | To send a sample order to the Function, run: 136 | 137 | ```bash 138 | curl -X POST ${FUNCTION_URL} -k \ 139 | -H "Content-Type: application/json" -d \ 140 | '{ 141 | "consignmentCode": "76272727", 142 | "orderCode": "76272725", 143 | "consignmentStatus": "PICKUP_COMPLETE" 144 | }' 145 | ``` 146 | 147 | To retrieve all orders saved in storage, run: 148 | 149 | ```bash 150 | curl -X GET ${FUNCTION_URL} -k 151 | ``` 152 | 153 | **FUNCTION_URL** is the URL of the running Function. See the [tutorial on exposing a Function with an API Rule](https://github.com/kyma-project/kyma/blob/release-1.24/docs/serverless/08-02-expose-function.md) for reference. 154 | -------------------------------------------------------------------------------- /prometheus/assets/monitoring-tutorials.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Kubernetes and Helm


Deploy and configure the kube-prometheus stack
Kubernetes and Helm...
Prometheus 


Define an
alerting rule
Prometheus...
Grafana


Create a
Gauge
dashboard type
Grafana...
Localhost and
Prometheus UI


Deploy a service,
expose and observe
the custom metric
Localhost and...
Text is not SVG - cannot display
-------------------------------------------------------------------------------- /prometheus/prometheus.md: -------------------------------------------------------------------------------- 1 | # Install a custom kube-prometheus-stack 2 | 3 | ## Overview 4 | 5 | The Kyma monitoring stack brings limited configuration options in contrast to the upstream [`kube-prometheus-stack`](https://github.com/prometheus-community/helm-charts/blob/main/charts/kube-prometheus-stack) chart. Modifications might be reset at the next upgrade cycle. 6 | 7 | This tutorial outlines how to set up a [`kube-prometheus-stack`](https://github.com/prometheus-community/helm-charts/blob/main/charts/kube-prometheus-stack), including a basic setup of Prometheus, Grafana and the Alertmanager. 8 | 9 | > **CAUTION:** 10 | - This tutorial describes a basic setup that you should not use in production. Typically, a production setup needs further configuration, like optimizing the amount of data to scrape and the required resource footprint of the installation. To achieve qualities like [high availability](https://prometheus.io/docs/introduction/faq/#can-prometheus-be-made-highly-available), [scalability](https://prometheus.io/docs/introduction/faq/#i-was-told-prometheus-doesnt-scale), or [durable long-term storage](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage), you need a more advanced setup. 11 | - This example uses the latest Grafana version, which is under AGPL-3.0 and might not be free of charge for commercial usage. 12 | 13 | ## Prerequisites 14 | 15 | - Kyma as the target deployment environment. 16 | - The [Istio module](https://github.com/kyma-project/istio) is [enabled](https://kyma-project.io/#/02-get-started/01-quick-install). 17 | - Kubectl > 1.22.x 18 | - Helm 3.x 19 | 20 | ## Installation 21 | 22 | ### Preparation 23 | 1. Export your Namespace as a variable. Replace the `{namespace}` placeholder in the following command and run it: 24 | 25 | ```bash 26 | export K8S_NAMESPACE="{namespace}" 27 | ``` 28 | 1. If you haven't created the Namespace yet, now is the time to do so: 29 | ```bash 30 | kubectl create namespace $K8S_NAMESPACE 31 | ``` 32 | >**Note**: This Namespace must have **no** Istio sidecar injection enabled; that is, there must be no `istio-injection` label present on the Namespace. The Helm chart deploys jobs that will not succeed when Isto sidecar injection is enabled. 33 | 34 | 1. Export the Helm release name that you want to use. It can be any name, but be aware that all resources in the cluster will be prefixed with that name. Run the following command: 35 | ```bash 36 | export HELM_PROM_RELEASE="prometheus" 37 | ``` 38 | 39 | 1. Update your Helm installation with the required Helm repository: 40 | 41 | ```bash 42 | helm repo add prometheus-community https://prometheus-community.github.io/helm-charts 43 | helm repo update 44 | ``` 45 | 46 | ### Install the kube-prometheus-stack 47 | 48 | 1. Run the Helm upgrade command, which installs the chart if it's not present yet. At the end of the command, change the Grafana admin password to some value of your choice. 49 | ```bash 50 | helm upgrade --install -n ${K8S_NAMESPACE} ${HELM_PROM_RELEASE} prometheus-community/kube-prometheus-stack -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/values.yaml --set grafana.adminPassword=myPwd 51 | ``` 52 | 53 | 2. You can use the [values.yaml](./values.yaml) provided with this tutorial, which contains customized settings deviating from the default settings, or create your own one. 54 | The provided `values.yaml` covers the following adjustments: 55 | - Client certificate injection to support scraping of workload secured with Istio strict mTLS 56 | - Active scraping of workload annotated with prometheus.io/scrape 57 | - Basic configuration of data persistence with retention 58 | - Basic resource limits for involved components 59 | 60 | ### Activate scraping of Istio metrics & Grafana dashboards 61 | 62 | 1. To configure Prometheus for scraping of the Istio-specific metrics from any istio-proxy running in the cluster, deploy a PodMonitor, which scrapes any Pod that has a port with name `.*-envoy-prom` exposed. 63 | 64 | ```bash 65 | kubectl -n ${K8S_NAMESPACE} apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/istio/podmonitor-istio-proxy.yaml 66 | ``` 67 | 68 | 2. Deploy a ServiceMonitor definition for the central metrics of the `istiod` deployment: 69 | 70 | ```bash 71 | kubectl -n ${K8S_NAMESPACE} apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/istio/servicemonitor-istiod.yaml 72 | ``` 73 | 74 | 3. Get the latest versions of the Istio-specific dashboards. 75 | Grafana is configured to load dashboards dynamically from ConfigMaps in the cluster, so Istio-specific dashboards can be applied as well. 76 | Either follow the [Istio quick start instructions](https://istio.io/latest/docs/ops/integrations/grafana/#option-1-quick-start), or take the prepared ones with the following command: 77 | 78 | ```bash 79 | kubectl -n ${K8S_NAMESPACE} apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/istio/configmap-istio-grafana-dashboards.yaml 80 | kubectl -n ${K8S_NAMESPACE} apply -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/istio/configmap-istio-services-grafana-dashboards.yaml 81 | ``` 82 | 83 | > **NOTE:** This setup collects all Istio metrics on a Pod level, which can lead to cardinality issues. Because metrics are only needed on service level, for setups having a bigger amount of workloads deployed, it is recommended to use a setup based on federation as described in the [Istio documentation](https://istio.io/latest/docs/ops/best-practices/observability/#using-prometheus-for-production-scale-monitoring). 84 | 85 | ### Verify the installation 86 | 87 | 1. You should see several Pods coming up in the Namespace, especially Prometheus and Alertmanager. Assure that all Pods have the "Running" state. 88 | 2. Browse the Prometheus dashboard and verify that all "Status->Targets" are healthy. The following command exposes the dashboard on `http://localhost:9090`: 89 | ```bash 90 | kubectl -n ${K8S_NAMESPACE} port-forward $(kubectl -n ${K8S_NAMESPACE} get service -l app=kube-prometheus-stack-prometheus -oname) 9090 91 | ``` 92 | 3. Browse the Grafana dashboard and verify that the dashboards are showing data. The user `admin` is preconfigured in the Helm chart; the password was provided in your `helm install` command. The following command exposes the dashboard on `http://localhost:3000`: 93 | ```bash 94 | kubectl -n ${K8S_NAMESPACE} port-forward svc/${HELM_PROM_RELEASE}-grafana 3000:80 95 | ``` 96 | 97 | ### Deploy a custom workload and scrape it 98 | 99 | Follow the tutorial [monitoring-custom-metrics](./monitoring-custom-metrics/), but use the steps above to verify that the metrics are collected. 100 | 101 | ### Scrape workload via annotations 102 | 103 | Instead of defining a ServiceMonitor per workload for setting up custom metric scraping, you can use a simplified way based on annotations. The used [values.yaml](./values.yaml) defines an `additionalScrapeConfig`, which scrapes all Pods and services that have the following annotations: 104 | 105 | ```yaml 106 | prometheus.io/scrape: "true" # mandatory to enable automatic scraping 107 | prometheus.io/scheme: https # optional, default is "http" if no Istio sidecar is used. When using a sidecar (Pod has label `security.istio.io/tlsMode=istio`), the default is "https". Use "https" to scrape workloads using Istio client certificates. 108 | prometheus.io/port: "1234" # optional, configure the port under which the metrics are exposed 109 | prometheus.io/path: /myMetrics # optional, configure the path under which the metrics are exposed 110 | ``` 111 | 112 | You can try it out by removing the ServiceMonitor from the previous example and instead providing the annotations to the Service manifest. 113 | 114 | ### Set up alerting 115 | 116 | 1. You can connect the Alertmanager to your notification channel (for instance, Slack or VictorOps) by providing an [Alertmanager configuration](https://prometheus.io/docs/alerting/latest/configuration/#configuration-file) to the `alertmanager.config` value. 117 | 118 | The [alertmanager-values.yaml](./alertmanager-values.yaml) example provides a configuration that sends notifications for alerts with high severity to a Slack channel. To deploy it, download the file, adapt ``, `` and `` to your environment, and run the Helm upgrade command to deploy the configuration: 119 | ```bash 120 | helm upgrade --install -n ${K8S_NAMESPACE} ${HELM_PROM_RELEASE} prometheus-community/kube-prometheus-stack -f https://raw.githubusercontent.com/kyma-project/examples/main/prometheus/values.yaml -f ./alertmanager-values.yaml --set grafana.adminPassword=myPwd 121 | ``` 122 | 123 | 2. Follow the tutorial [monitoring-alert-rules](./monitoring-alert-rules/) to set up an alerting rule on Prometheus. 124 | 125 | ### Set up Grafana dashboards 126 | 127 | Follow the tutorial [monitoring-grafana-dashboard](./monitoring-grafana-dashboard) to learn how to visualize your metrics in a Grafana dashboard. 128 | 129 | ### Cleanup 130 | 131 | To remove the installation from the cluster, call Helm: 132 | 133 | ```bash 134 | helm delete -n ${K8S_NAMESPACE} ${HELM_PROM_RELEASE} 135 | ``` 136 | -------------------------------------------------------------------------------- /LICENSES/Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 10 | 11 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 12 | 13 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 14 | 15 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 16 | 17 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 18 | 19 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 20 | 21 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 22 | 23 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 24 | 25 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 26 | 27 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 28 | 29 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 30 | 31 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 32 | 33 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 34 | 35 | (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and 36 | 37 | (b) You must cause any modified files to carry prominent notices stating that You changed the files; and 38 | 39 | (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 40 | 41 | (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 42 | 43 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 44 | 45 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 46 | 47 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 48 | 49 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 50 | 51 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 52 | 53 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 54 | 55 | END OF TERMS AND CONDITIONS 56 | 57 | APPENDIX: How to apply the Apache License to your work. 58 | 59 | To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 60 | 61 | Copyright [yyyy] [name of copyright owner] 62 | 63 | Licensed under the Apache License, Version 2.0 (the "License"); 64 | you may not use this file except in compliance with the License. 65 | You may obtain a copy of the License at 66 | 67 | http://www.apache.org/licenses/LICENSE-2.0 68 | 69 | Unless required by applicable law or agreed to in writing, software 70 | distributed under the License is distributed on an "AS IS" BASIS, 71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 72 | See the License for the specific language governing permissions and 73 | limitations under the License. 74 | --------------------------------------------------------------------------------