From b69455ba263fd86e087585d3153e1eba3d29a16d Mon Sep 17 00:00:00 2001 From: Carsten Schafer Date: Mon, 24 Jun 2024 13:56:13 -0400 Subject: [PATCH 1/5] Fix some spelling issues --- src/cgw_connection_server.rs | 6 +++--- src/cgw_db_accessor.rs | 10 +++++----- src/cgw_metrics.rs | 4 ++-- src/cgw_remote_discovery.rs | 2 +- src/main.rs | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cgw_connection_server.rs b/src/cgw_connection_server.rs index 8d947e8..063185b 100644 --- a/src/cgw_connection_server.rs +++ b/src/cgw_connection_server.rs @@ -1158,7 +1158,7 @@ impl CGWConnectionServer { error!("Failed to construct foreign_infra_connection message"); } - debug!("Detected foreign infra {} connetion. Group: {}, Group Shard Owner: {}", device_mac.to_hex_string(), group_id, group_owner_id); + debug!("Detected foreign infra {} connection. Group: {}, Group Shard Owner: {}", device_mac.to_hex_string(), group_id, group_owner_id); } } else { if let Ok(resp) = cgw_construct_unassigned_infra_connection_msg( @@ -1171,7 +1171,7 @@ impl CGWConnectionServer { } debug!( - "Detected unassigned infra {} connetion.", + "Detected unassigned infra {} connection.", device_mac.to_hex_string() ); } @@ -1241,7 +1241,7 @@ impl CGWConnectionServer { } debug!( - "Detected unassigned infra {} connetion.", + "Detected unassigned infra {} connection.", device_mac.to_hex_string() ); } diff --git a/src/cgw_db_accessor.rs b/src/cgw_db_accessor.rs index 50227e3..c7df2ff 100644 --- a/src/cgw_db_accessor.rs +++ b/src/cgw_db_accessor.rs @@ -61,7 +61,7 @@ impl CGWDBAccessor { pass = app_args.db_password ); debug!( - "Trying to connect to remote db ({}:{})...\nConn args {}", + "Trying to connect to DB ({}:{})...\nConn args {}", app_args.db_host, app_args.db_port, conn_str ); @@ -69,18 +69,18 @@ impl CGWDBAccessor { Ok((cl, conn)) => (cl, conn), Err(e) => { error!( - "Failed to establish connection with remote DB, reason: {:?}", + "Failed to establish connection with DB, reason: {:?}", e ); return Err(Error::DbAccessor( - "Failed to establish connection with remote DB", + "Failed to establish connection with DB", )); } }; tokio::spawn(async move { if let Err(e) = connection.await { - let err_msg = format!("Connection to remote DB broke up: {}", e); + let err_msg = format!("Connection to DB broken: {}", e); error!("{}", err_msg); CGWMetrics::get_ref() .change_component_health_status( @@ -100,7 +100,7 @@ impl CGWDBAccessor { .await; }); - info!("Connectiong to SQL DB has been established!"); + info!("Connection to SQL DB has been established!"); Ok(CGWDBAccessor { cl: client }) } diff --git a/src/cgw_metrics.rs b/src/cgw_metrics.rs index 14a6ce3..405a4b1 100644 --- a/src/cgw_metrics.rs +++ b/src/cgw_metrics.rs @@ -123,7 +123,7 @@ impl CGWMetrics { return Ok(()); } - debug!("Staring metrics engine..."); + debug!("Starting metrics engine..."); *started = true; @@ -164,7 +164,7 @@ impl CGWMetrics { warp::serve(routes).run(([0, 0, 0, 0], port)).await; }); - debug!("Metrics engine's been started!"); + debug!("Metrics engine started!"); Ok(()) } diff --git a/src/cgw_remote_discovery.rs b/src/cgw_remote_discovery.rs index 03102bf..e25a780 100644 --- a/src/cgw_remote_discovery.rs +++ b/src/cgw_remote_discovery.rs @@ -290,7 +290,7 @@ impl CGWRemoteDiscovery { .await; }); - info!("Connectiong to REDIS DB has been established!"); + info!("Connection to REDIS DB has been established!"); Ok(rc) } diff --git a/src/main.rs b/src/main.rs index 0aaeb82..265a896 100644 --- a/src/main.rs +++ b/src/main.rs @@ -513,7 +513,7 @@ impl AppCore { } async fn server_loop(app_core: Arc) -> Result<()> { - debug!("sever_loop entry"); + debug!("server_loop entry"); debug!( "Starting WSS server, listening at {}:{}", From 3438c5916d6cf54b9d7b9d026ee2e08f9a82e6c1 Mon Sep 17 00:00:00 2001 From: Carsten Schafer Date: Mon, 24 Jun 2024 15:14:50 -0400 Subject: [PATCH 2/5] Add helm chart --- helm/.gitignore | 1 + helm/.helmignore | 22 +++++ helm/Chart.yaml | 5 ++ helm/README.md | 100 +++++++++++++++++++++++ helm/templates/_helpers.tpl | 42 ++++++++++ helm/templates/deployment.yaml | 125 +++++++++++++++++++++++++++++ helm/templates/ingress.yaml | 61 ++++++++++++++ helm/templates/psp.yaml | 28 +++++++ helm/templates/pvc.yaml | 27 +++++++ helm/templates/role.yaml | 16 ++++ helm/templates/rolebinding.yaml | 15 ++++ helm/templates/secret-certs.yaml | 15 ++++ helm/templates/secret-env.yaml | 17 ++++ helm/templates/secret-regcred.yaml | 21 +++++ helm/templates/service.yaml | 48 +++++++++++ helm/values.yaml | 122 ++++++++++++++++++++++++++++ utils/client_simulator/runsingle | 36 +++++++++ utils/client_simulator/single.py | 109 +++++++++++++++++++++++++ 18 files changed, 810 insertions(+) create mode 100644 helm/.gitignore create mode 100644 helm/.helmignore create mode 100644 helm/Chart.yaml create mode 100644 helm/README.md create mode 100644 helm/templates/_helpers.tpl create mode 100644 helm/templates/deployment.yaml create mode 100644 helm/templates/ingress.yaml create mode 100644 helm/templates/psp.yaml create mode 100644 helm/templates/pvc.yaml create mode 100644 helm/templates/role.yaml create mode 100644 helm/templates/rolebinding.yaml create mode 100644 helm/templates/secret-certs.yaml create mode 100644 helm/templates/secret-env.yaml create mode 100644 helm/templates/secret-regcred.yaml create mode 100644 helm/templates/service.yaml create mode 100644 helm/values.yaml create mode 100755 utils/client_simulator/runsingle create mode 100755 utils/client_simulator/single.py diff --git a/helm/.gitignore b/helm/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/helm/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/helm/.helmignore b/helm/.helmignore new file mode 100644 index 0000000..50af031 --- /dev/null +++ b/helm/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..706c442 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +appVersion: "1.0.0" +description: A CGW Helm chart for Kubernetes +name: cgw +version: 0.1.0 diff --git a/helm/README.md b/helm/README.md new file mode 100644 index 0000000..48f89eb --- /dev/null +++ b/helm/README.md @@ -0,0 +1,100 @@ +# cgw + +This Helm chart helps to deploy OpenLAN CGW (further on refered as __Gateway__) to the Kubernetes clusters. It is mainly used in [assembly chart](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/cgwchart) as Gateway requires other services as dependencies that are considered in that Helm chart. This chart is purposed to define deployment logic close to the application code itself and define default values that could be overriden during deployment. + + +## TL;DR; + +```bash +$ helm install . +``` + +## Introduction + +This chart bootstraps the Gateway on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Installing the Chart + +Currently this chart is not assembled in charts archives, so [helm-git](https://github.com/aslafy-z/helm-git) is required for remote the installation + +To install the chart with the release name `my-release`: + +```bash +$ helm install --name my-release git+https://github.com/Telecominfraproject/openlan-cgw@helm/cgw-0.1.0.tgz?ref=master +``` + +The command deploys the Gateway on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```bash +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +The following table lists the configurable parameters of the chart and their default values. If Default value is not listed in the table, please refer to the [Values](values.yaml) files for details. + +| Parameter | Type | Description | Default | +|-----------|------|-------------|---------| +| replicaCount | number | Amount of replicas to be deployed | `1` | +| strategyType | string | Application deployment strategy | `'Recreate'` | +| nameOverride | string | Override to be used for application deployment | | +| fullnameOverride | string | Override to be used for application deployment (has priority over nameOverride) | | +| images.cgw.repository | string | Docker image repository | | +| images.cgw.tag | string | Docker image tag | `'master'` | +| images.cgw.pullPolicy | string | Docker image pull policy | `'Always'` | +| services.cgw.type | string | Gateway service type | `'LoadBalancer'` | +| services.cgw.ports.websocket.servicePort | number | Websocket endpoint port to be exposed on service | `15002` | +| services.cgw.ports.websocket.targetPort | number | Websocket endpoint port to be targeted by service | `15002` | +| services.cgw.ports.websocket.protocol | string | Websocket endpoint protocol | `'TCP'` | +| services.cgw.ports.restapi.servicePort | number | REST API endpoint port to be exposed on service | `16002` | +| services.cgw.ports.restapi.targetPort | number | REST API endpoint port to be targeted by service | `16002` | +| services.cgw.ports.restapi.protocol | string | REST API endpoint protocol | `'TCP'` | +| services.cgw.ports.restapiinternal.servicePort | string | Internal REST API endpoint port to be exposed on service | `17002` | +| services.cgw.ports.restapiinternal.targetPort | number | Internal REST API endpoint port to be targeted by service | `17002` | +| services.cgw.ports.restapiinternal.protocol | string | Internal REST API endpoint protocol | `'TCP'` | +| services.cgw.ports.fileuploader.servicePort | string | Fileuploader endpoint port to be exposed on service | `16003` | +| services.cgw.ports.fileuploader.targetPort | number | Fileuploader endpoint port to be targeted by service | `16003` | +| services.cgw.ports.fileuploader.protocol | string | Fileuploader endpoint protocol | `'TCP'` | +| checks.cgw.liveness.httpGet.path | string | Liveness check path to be used | `'/'` | +| checks.cgw.liveness.httpGet.port | number | Liveness check port to be used (should be pointint to ALB endpoint) | `16102` | +| checks.cgw.readiness.httpGet.path | string | Readiness check path to be used | `'/'` | +| checks.cgw.readiness.httpGet.port | number | Readiness check port to be used (should be pointint to ALB endpoint) | `16102` | +| ingresses.restapi.enabled | boolean | Defines if REST API endpoint should be exposed via Ingress controller | `False` | +| ingresses.restapi.hosts | array | List of hosts for exposed REST API | | +| ingresses.restapi.paths | array | List of paths to be exposed for REST API | | +| ingresses.fileuploader.enabled | boolean | Defines if Fileuploader endpoint should be exposed via Ingress controller | `False` | +| ingresses.fileuploader.hosts | array | List of hosts for exposed Fileuploader | | +| ingresses.fileuploader.paths | array | List of paths for exposed Fileuploader | | +| volumes.cgw | array | Defines list of volumes to be attached to the Gateway | | +| persistence.enabled | boolean | Defines if the Gateway requires Persistent Volume (required for permanent files storage and SQLite DB if enabled) | `True` | +| persistence.accessModes | array | Defines PV access modes | | +| persistence.size | string | Defines PV size | `'10Gi'` | +| public_env_variables | hash | Defines list of environment variables to be passed to the Gateway | | +| existingCertsSecret | string | Existing Kubernetes secret containing all required certificates and private keys for microservice operation. If set, certificates from `certs` key are ignored | `""` | +| certs | hash | Defines files (keys and certificates) that should be passed to the Gateway (PEM format is adviced to be used) (see `volumes.cgw` on where it is mounted). If `existingCertsSecret` is set, certificates passed this way will not be used. | | +| certsCAs | hash | Defines files with CAs that should be passed to the Gateway (see `volumes.cgw` on where it is mounted) | | + + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```bash +$ helm install --name my-release --set replicaCount=1 . +``` + +The above command sets that only 1 instance of your app should be running + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install --name my-release -f values.yaml . +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) as a base for customization. diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..1bc2636 --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,42 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "cgw.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "cgw.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cgw.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "cgw.ingress.apiVersion" -}} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" -}} + {{- print "networking.k8s.io/v1" -}} + {{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}} + {{- print "networking.k8s.io/v1beta1" -}} + {{- else -}} + {{- print "extensions/v1beta1" -}} + {{- end -}} +{{- end -}} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 0000000..f229524 --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,125 @@ +{{- $root := . -}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "cgw.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + helm.sh/chart: {{ include "cgw.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: {{ .Values.strategyType }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- with .Values.services.cgw.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + template: + metadata: + annotations: + {{- if .Values.podSecurityPolicy.enabled }} + kubernetes.io/psp: {{ include "cgw.fullname" . }}-{{ .Release.Namespace }}-cgw-unsafe-sysctl + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- with .Values.services.cgw.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + + containers: + + - name: cgw + image: "{{ .Values.images.cgw.repository }}:{{ .Values.images.cgw.tag }}" + imagePullPolicy: {{ .Values.images.cgw.pullPolicy }} + + env: + - name: KUBERNETES_DEPLOYED + value: "{{ now }}" + {{- range $key, $value := .Values.public_env_variables }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- range $key, $value := .Values.secret_env_variables }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ include "cgw.fullname" $root }}-env + key: {{ $key }} + {{- end }} + + ports: + {{- range $port, $portValue := .Values.services.cgw.ports }} + - name: {{ $port }} + containerPort: {{ $portValue.targetPort }} + protocol: {{ $portValue.protocol }} + {{- end }} + + volumeMounts: + {{- range .Values.volumes.cgw }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- if .subPath }} + subPath: {{ .subPath }} + {{- end }} + {{- end }} + + {{- if .Values.checks.cgw.liveness }} + livenessProbe: + {{- toYaml .Values.checks.cgw.liveness | nindent 12 }} + {{- end }} + {{- if .Values.checks.cgw.readiness }} + readinessProbe: + {{- toYaml .Values.checks.cgw.readiness | nindent 12 }} + {{- end }} + + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + + imagePullSecrets: + {{- range $image, $imageValue := .Values.images }} + {{- if $imageValue.regcred }} + - name: {{ include "cgw.fullname" $root }}-{{ $image }}-regcred + {{- end }} + {{- end }} + + volumes: + {{- range $container, $containerVolumes := .Values.volumes }} + {{- range $containerVolumes }} + - name: {{ .name }} + {{- tpl .volumeDefinition $root | nindent 8 }} + {{- end }} + {{- end }} + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/templates/ingress.yaml b/helm/templates/ingress.yaml new file mode 100644 index 0000000..bb11868 --- /dev/null +++ b/helm/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- $root := . -}} +{{- range $ingress, $ingressValue := .Values.ingresses }} +{{- if $ingressValue.enabled }} +--- +apiVersion: {{ include "cgw.ingress.apiVersion" $root }} +kind: Ingress +metadata: + name: {{ include "cgw.fullname" $root }}-{{ $ingress }} + labels: + app.kubernetes.io/name: {{ include "cgw.name" $root }} + helm.sh/chart: {{ include "cgw.chart" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + app.kubernetes.io/managed-by: {{ $root.Release.Service }} + {{- with $ingressValue.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + +spec: + +{{- if $ingressValue.tls }} + tls: + {{- range $ingressValue.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ tpl .secretName $root }} + {{- end }} +{{- end }} + + rules: + {{- range $ingressValue.hosts }} + - host: {{ . | quote }} + http: + paths: + {{- range $ingressValue.paths }} + - path: {{ .path }} + {{- if $root.Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + pathType: {{ .pathType | default "ImplementationSpecific" }} + {{- end }} + backend: + {{- if $root.Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + service: + name: {{ include "cgw.fullname" $root }}-{{ .serviceName }} + port: + {{- if kindIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else }} + number: {{ .servicePort }} + {{- end }} + {{- else }} + serviceName: {{ include "cgw.fullname" $root }}-{{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} + {{- end }} + {{- end }} + +{{- end }} + +{{- end }} diff --git a/helm/templates/psp.yaml b/helm/templates/psp.yaml new file mode 100644 index 0000000..0c0d4ea --- /dev/null +++ b/helm/templates/psp.yaml @@ -0,0 +1,28 @@ +{{- if .Values.podSecurityPolicy.enabled }} +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "cgw.fullname" . }}-{{ .Release.Namespace }}-cgw-unsafe-sysctl + labels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + helm.sh/chart: {{ include "cgw.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + allowedUnsafeSysctls: + {{- range $unsafeSysctl := .Values.securityContext.sysctls }} + - {{ $unsafeSysctl.name }} + {{- end }} + privileged: false + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + runAsUser: + rule: RunAsAny + fsGroup: + rule: RunAsAny + volumes: + - '*' +{{- end }} diff --git a/helm/templates/pvc.yaml b/helm/templates/pvc.yaml new file mode 100644 index 0000000..a1245e6 --- /dev/null +++ b/helm/templates/pvc.yaml @@ -0,0 +1,27 @@ +{{- if .Values.persistence.enabled }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ template "cgw.fullname" . }}-pvc + labels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + helm.sh/chart: {{ include "cgw.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.persistence.annotations }} + annotations: +{{ toYaml . | indent 4 }} + {{- end }} +spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName }} +{{- end }} +{{- end }} diff --git a/helm/templates/role.yaml b/helm/templates/role.yaml new file mode 100644 index 0000000..ce31380 --- /dev/null +++ b/helm/templates/role.yaml @@ -0,0 +1,16 @@ +{{- if .Values.podSecurityPolicy.enabled }} +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "cgw.fullname" . }}-cgw-use-unsafe-sysctl +rules: +- apiGroups: + - policy + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ include "cgw.fullname" . }}-{{ .Release.Namespace }}-cgw-unsafe-sysctl +{{- end }} diff --git a/helm/templates/rolebinding.yaml b/helm/templates/rolebinding.yaml new file mode 100644 index 0000000..daa5cfa --- /dev/null +++ b/helm/templates/rolebinding.yaml @@ -0,0 +1,15 @@ +{{- if .Values.podSecurityPolicy.enabled }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "cgw.fullname" . }}-cgw-use-unsafe-sysctl-to-default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "cgw.fullname" . }}-cgw-use-unsafe-sysctl +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/helm/templates/secret-certs.yaml b/helm/templates/secret-certs.yaml new file mode 100644 index 0000000..3bf34db --- /dev/null +++ b/helm/templates/secret-certs.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: v1 +metadata: + labels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + helm.sh/chart: {{ include "cgw.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + name: {{ include "cgw.fullname" . }}-certs +kind: Secret +type: Opaque +data: + {{- range $key, $value := .Values.certs }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} diff --git a/helm/templates/secret-env.yaml b/helm/templates/secret-env.yaml new file mode 100644 index 0000000..21c8405 --- /dev/null +++ b/helm/templates/secret-env.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: v1 +metadata: + labels: + app.kubernetes.io/name: {{ include "cgw.name" . }} + helm.sh/chart: {{ include "cgw.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + name: {{ include "cgw.fullname" . }}-env +kind: Secret +type: Opaque +data: + # Secret env variables + {{- range $key, $value := .Values.secret_env_variables }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} + diff --git a/helm/templates/secret-regcred.yaml b/helm/templates/secret-regcred.yaml new file mode 100644 index 0000000..2685305 --- /dev/null +++ b/helm/templates/secret-regcred.yaml @@ -0,0 +1,21 @@ +{{- define "imagePullSecret" }} +{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .registry (printf "%s:%s" .username .password | b64enc) | b64enc }} +{{- end }} +{{- $root := . -}} +{{- range $image, $imageValue := .Values.images }} +{{- if $imageValue.regcred }} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/dockerconfigjson +metadata: + labels: + app.kubernetes.io/name: {{ include "cgw.name" $root }} + helm.sh/chart: {{ include "cgw.chart" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + app.kubernetes.io/managed-by: {{ $root.Release.Service }} + name: {{ include "cgw.fullname" $root }}-{{ $image }}-regcred +data: + .dockerconfigjson: {{ template "imagePullSecret" $imageValue.regcred }} +{{- end }} +{{- end }} diff --git a/helm/templates/service.yaml b/helm/templates/service.yaml new file mode 100644 index 0000000..16314c9 --- /dev/null +++ b/helm/templates/service.yaml @@ -0,0 +1,48 @@ +{{- $root := . -}} +{{- range $service, $serviceValue := .Values.services }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "cgw.fullname" $root }}-{{ $service }} + {{- with $serviceValue.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app.kubernetes.io/name: {{ include "cgw.name" $root }} + helm.sh/chart: {{ include "cgw.chart" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + app.kubernetes.io/managed-by: {{ $root.Release.Service }} + + {{- with $serviceValue.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + + {{- if $serviceValue.serviceMonitor }} + + {{- range $selector, $selectorValue := $serviceValue.serviceMonitor.serviceSelector }} + {{ $selector }}: {{ tpl $selectorValue $root }} + {{- end }} + {{- end }} +spec: + type: {{ $serviceValue.type }} + ports: + + {{- range $service_service, $service_value := $serviceValue.ports }} + - name: {{ $service_service }} + targetPort: {{ $service_value.targetPort }} + protocol: {{ $service_value.protocol }} + port: {{ $service_value.servicePort }} + {{- if and (eq "NodePort" $serviceValue.type) $service_value.nodePort }} + nodePort: {{ $service_value.nodePort }} + {{- end }} + {{- end }} + selector: + app.kubernetes.io/name: {{ include "cgw.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + {{- with $serviceValue.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + +{{- end }} diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..a320ece --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,122 @@ +replicaCount: 1 +strategyType: Recreate +revisionHistoryLimit: 2 + +nameOverride: "" +fullnameOverride: "" + +images: + cgw: + repository: tip-tip-wlan-cloud-ucentral.jfrog.io/cgw + tag: carsten + pullPolicy: Always +# regcred: +# registry: tip-tip-wlan-cloud-ucentral.jfrog.io +# username: username +# password: password + +services: + cgw: + type: ClusterIP + ports: + websocket: + servicePort: 15002 + targetPort: 15002 + protocol: TCP + grpc: + servicePort: 15051 + targetPort: 50051 + protocol: TCP + +checks: + cgw: {} +# TODO: what should this be for CGW? +# liveness: +# httpGet: +# path: / +# port: 16102 +# readiness: +# exec: +# command: +# - /readiness_check + +ingresses: {} + +volumes: + cgw: + - name: certs + mountPath: /cgw-data/certs + volumeDefinition: | + secret: + secretName: {{ if .Values.existingCertsSecret }}{{ .Values.existingCertsSecret }}{{ else }}{{ include "cgw.fullname" . }}-certs{{ end }} + - name: persist + mountPath: /cgw-data/persist + volumeDefinition: | + persistentVolumeClaim: + claimName: {{ template "cgw.fullname" . }}-pvc + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # requests: + # cpu: 100m + # memory: 128Mi + # limits: + # cpu: 100m + # memory: 128Mi + +securityContext: + fsGroup: 1000 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +podAnnotations: {} + +podSecurityPolicy: + enabled: false + +persistence: + enabled: true + # storageClassName: "-" + accessModes: + - ReadWriteOnce + size: 10Gi + annotations: {} + +# Application +public_env_variables: + CGW_ROOT: /cgw-data + CGW_WSS_CAS: "/cgw-data/certs/cas.pem" + CGW_WSS_CERT: "/cgw-data/certs/websocket-cert.pem" + CGW_WSS_KEY: "/cgw-data/certs/websocket-key.pem" + CGW_DB_HOST: "psql" + CGW_DB_IP: "127.0.0.1" + CGW_DB_PORT: "5432" + CGW_DB_NAME: "cgw" + CGW_DB_USERNAME: "cgw" + CGW_KAFKA_HOST: "kafka" + CGW_KAFKA_IP: "127.0.0.1" + CGW_KAFKA_PORT: "9092" + CGW_REDIS_IP: "127.0.0.1" + CGW_REDIS_HOST: "redis" + CGW_REDIS_PORT: "9092" + CGW_KAFKA_CONSUME_TOPIC: "CnC" + CGW_KAFKA_PRODUCE_TOPIC: "CnC_Res" + +secret_env_variables: + CGW_DB_PASSWORD: "123" + +# NOTE: List of required certificates may be found in "certs" key. Alternative way to pass required certificates is to create external secret with all required certificates and set secret name in "existingCertsSecret" key. Details may be found in https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/cgwchart#tldr +existingCertsSecret: "" + +certs: + root.pem: "" + websocket-cert.pem: "" + websocket-key.pem: "" + cas.pem: "" diff --git a/utils/client_simulator/runsingle b/utils/client_simulator/runsingle new file mode 100755 index 0000000..b081674 --- /dev/null +++ b/utils/client_simulator/runsingle @@ -0,0 +1,36 @@ +#!/bin/bash +[ -z "$URL" ] && URL="wss://cgw-devcgw.cicd.lab.wlan.tip.build:15002" +#URL="wss://cgw-cgw:15002" +[ -z "$MAC" ] && MAC="11:22:AA:BB:CC:13" +MSG_INTERVAL=10 +MSG_SIZE=1000 +CA_CERT_PATH=$(pwd)/../cert_generator/certs/ca +CA_CERT_PATH=./tipcerts +CLIENT_CERT_PATH=$(pwd)/../cert_generator/certs/client +CLIENT_CERT_PATH=./tipcerts +python3 single.py --mac "$MAC" \ + --server "$URL" \ + --no-cert-check \ + --ca-cert "$CA_CERT_PATH/ca.crt" \ + --client-certs-path "$CLIENT_CERT_PATH" \ + --msg-interval "$MSG_INTERVAL" \ + --payload-size "$MSG_SIZE" +exit + + +MAC="11:22:AA:BB:CC:XX" +COUNT=20 +URL="wss://cgw-devcgw.cicd.lab.wlan.tip.build:15002" +MSG_INTERVAL=10 +MSG_SIZE=1000 +CA_CERT_PATH=$(pwd)/../cert_generator/certs/ca +#CA_CERT_PATH=./certs/ca +CLIENT_CERT_PATH=$(pwd)/../cert_generator/certs/client +#CLIENT_CERT_PATH=./certs/client +python3 main.py --mac ${MAC} -N ${COUNT} \ + --server ${URL} \ + --no-cert-check \ + --ca-cert $CA_CERT_PATH/ca.crt \ + --client-certs-path $CLIENT_CERT_PATH \ + --msg-interval ${MSG_INTERVAL} \ + --payload-size ${MSG_SIZE} diff --git a/utils/client_simulator/single.py b/utils/client_simulator/single.py new file mode 100755 index 0000000..24e1456 --- /dev/null +++ b/utils/client_simulator/single.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +from dataclasses import dataclass +from typing import List +import argparse +import random +import json +import re +import os + +from src.simulation_runner import Device + + +@dataclass +class Args: + mac: str + ca_path: str + cert_path: str + msg_size: int + msg_interval: int + server_proto: str = "ws" + server_address: str = "localhost" + server_port: int = 15002 + cert_check: bool = True + + @property + def server(self): + return f"{self.server_proto}://{self.server_address}:{self.server_port}" + + +def parse_msg_size(input: str) -> int: + match = re.match(r"^(\d+)([kKmM]?)$", input) + if match is None: + raise ValueError(f"Unable to parse message size \"{input}\"") + num, prefix = match.groups() + num = int(num) + if prefix and prefix in "kK": + num *= 1000 + elif prefix and prefix in "mM": + num *= 1000000 + return num + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Used to simulate a single client that connects to a single server.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument("-s", "--server", metavar="ADDRESS", required=True, + default="ws://localhost:15002", + help="server address") + parser.add_argument("-m", "--mac", metavar="XX:XX:XX:XX:XX:XX", required=True, + default="", + help="the mask determines what MAC addresses will be used by the client.") + parser.add_argument("-a", "--ca-cert", metavar="CERT", + default="./certs/ca/ca.crt", + help="path to CA certificate") + parser.add_argument("-c", "--client-certs-path", metavar="PATH", + default="./certs/client", + help="path to client certificates directory") + parser.add_argument("-C", "--no-cert-check", action='store_true', + default=False, + help="do not check certificate") + parser.add_argument("-t", "--msg-interval", metavar="SECONDS", type=int, + default=10, + help="time between client messages to gw") + parser.add_argument("-p", "--payload-size", metavar="SIZE", type=str, + default="1k", + help="size of each client message") + + parsed_args = parser.parse_args() + + args = Args(mac=parsed_args.mac, + ca_path=parsed_args.ca_cert, + cert_path=parsed_args.client_certs_path, + msg_interval=parsed_args.msg_interval, + msg_size=parse_msg_size(parsed_args.payload_size), + cert_check=not parsed_args.no_cert_check + ) + + # PROTO :// ADDRESS : PORT + # TODO: fixme the host portion can contain a lot more than just these characters! + match = re.match(r"(?:(wss?)://)?([\d\w\.-]+):?(\d+)?", parsed_args.server) + if match is None: + raise ValueError(f"Unable to parse server address {parsed_args.server}") + proto, addr, port = match.groups() + if proto is not None: + args.server_proto = proto + if addr is not None: + args.server_address = addr + if port is not None: + args.server_port = port + return args + + +def main(args): + mac = args.mac + device = Device(mac, args.server, args.ca_path, args.msg_interval, args.msg_size, + os.path.join(args.cert_path, f"{mac}.crt"), + os.path.join(args.cert_path, f"{mac}.key"), + args.cert_check, + None, None) + print(f"Server: {device.server_addr}") + print(f"MAC: {mac}") + device.single_run() + + +if __name__ == "__main__": + args = parse_args() + main(args) From 2098eb98d5658e921546524a42ad4bc72583c5e9 Mon Sep 17 00:00:00 2001 From: Carsten Schafer Date: Mon, 24 Jun 2024 15:15:17 -0400 Subject: [PATCH 3/5] simulator tweaks --- helm/README.md | 34 +++++------- helm/values.yaml | 55 ++++++++++--------- utils/client_simulator/.gitignore | 2 + utils/client_simulator/Dockerfile | 12 +--- utils/client_simulator/runsingle | 21 +------ .../client_simulator/src/simulation_runner.py | 28 +++++++++- utils/client_simulator/src/utils.py | 8 ++- 7 files changed, 81 insertions(+), 79 deletions(-) diff --git a/helm/README.md b/helm/README.md index 48f89eb..863b68e 100644 --- a/helm/README.md +++ b/helm/README.md @@ -50,34 +50,26 @@ The following table lists the configurable parameters of the chart and their def | images.cgw.repository | string | Docker image repository | | | images.cgw.tag | string | Docker image tag | `'master'` | | images.cgw.pullPolicy | string | Docker image pull policy | `'Always'` | -| services.cgw.type | string | Gateway service type | `'LoadBalancer'` | +| services.cgw.type | string | Gateway service type | `'ClusterIP'` | | services.cgw.ports.websocket.servicePort | number | Websocket endpoint port to be exposed on service | `15002` | | services.cgw.ports.websocket.targetPort | number | Websocket endpoint port to be targeted by service | `15002` | | services.cgw.ports.websocket.protocol | string | Websocket endpoint protocol | `'TCP'` | -| services.cgw.ports.restapi.servicePort | number | REST API endpoint port to be exposed on service | `16002` | -| services.cgw.ports.restapi.targetPort | number | REST API endpoint port to be targeted by service | `16002` | -| services.cgw.ports.restapi.protocol | string | REST API endpoint protocol | `'TCP'` | -| services.cgw.ports.restapiinternal.servicePort | string | Internal REST API endpoint port to be exposed on service | `17002` | -| services.cgw.ports.restapiinternal.targetPort | number | Internal REST API endpoint port to be targeted by service | `17002` | -| services.cgw.ports.restapiinternal.protocol | string | Internal REST API endpoint protocol | `'TCP'` | -| services.cgw.ports.fileuploader.servicePort | string | Fileuploader endpoint port to be exposed on service | `16003` | -| services.cgw.ports.fileuploader.targetPort | number | Fileuploader endpoint port to be targeted by service | `16003` | -| services.cgw.ports.fileuploader.protocol | string | Fileuploader endpoint protocol | `'TCP'` | -| checks.cgw.liveness.httpGet.path | string | Liveness check path to be used | `'/'` | -| checks.cgw.liveness.httpGet.port | number | Liveness check port to be used (should be pointint to ALB endpoint) | `16102` | -| checks.cgw.readiness.httpGet.path | string | Readiness check path to be used | `'/'` | -| checks.cgw.readiness.httpGet.port | number | Readiness check port to be used (should be pointint to ALB endpoint) | `16102` | -| ingresses.restapi.enabled | boolean | Defines if REST API endpoint should be exposed via Ingress controller | `False` | -| ingresses.restapi.hosts | array | List of hosts for exposed REST API | | -| ingresses.restapi.paths | array | List of paths to be exposed for REST API | | -| ingresses.fileuploader.enabled | boolean | Defines if Fileuploader endpoint should be exposed via Ingress controller | `False` | -| ingresses.fileuploader.hosts | array | List of hosts for exposed Fileuploader | | -| ingresses.fileuploader.paths | array | List of paths for exposed Fileuploader | | +| services.cgw.ports.metrics.servicePort | number | Metrics API endpoint port to be exposed on service | `15003` | +| services.cgw.ports.metrics.targetPort | number | Metrics API endpoint port to be targeted by service | `8080` | +| services.cgw.ports.metrics.protocol | string | Metrics API endpoint protocol | `'TCP'` | +| services.cgw.ports.grpc.servicePort | string | Internal REST API endpoint port to be exposed on service | `15051` | +| services.cgw.ports.grpc.targetPort | number | Internal REST API endpoint port to be targeted by service | `50051` | +| services.cgw.ports.grpc.protocol | string | Internal REST API endpoint protocol | `'TCP'` | +| checks.cgw.liveness.httpGet.path | string | Liveness check path to be used | `'/health'` | +| checks.cgw.liveness.httpGet.port | number | Liveness check port to be used (should be pointint to ALB endpoint) | `15003` | +| checks.cgw.readiness.httpGet.path | string | Readiness check path to be used | `'/health'` | +| checks.cgw.readiness.httpGet.port | number | Readiness check port to be used (should be pointint to ALB endpoint) | `15003` | | volumes.cgw | array | Defines list of volumes to be attached to the Gateway | | | persistence.enabled | boolean | Defines if the Gateway requires Persistent Volume (required for permanent files storage and SQLite DB if enabled) | `True` | | persistence.accessModes | array | Defines PV access modes | | | persistence.size | string | Defines PV size | `'10Gi'` | -| public_env_variables | hash | Defines list of environment variables to be passed to the Gateway | | +| public\_env\_variables | hash | Defines list of environment variables to be passed to the Gateway via ConfigMaps | | +| secret\_env\_variables | hash | Defines list of secret environment variables to be passed to the Gateway via secrets | | | existingCertsSecret | string | Existing Kubernetes secret containing all required certificates and private keys for microservice operation. If set, certificates from `certs` key are ignored | `""` | | certs | hash | Defines files (keys and certificates) that should be passed to the Gateway (PEM format is adviced to be used) (see `volumes.cgw` on where it is mounted). If `existingCertsSecret` is set, certificates passed this way will not be used. | | | certsCAs | hash | Defines files with CAs that should be passed to the Gateway (see `volumes.cgw` on where it is mounted) | | diff --git a/helm/values.yaml b/helm/values.yaml index a320ece..f7ec49c 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -8,7 +8,7 @@ fullnameOverride: "" images: cgw: repository: tip-tip-wlan-cloud-ucentral.jfrog.io/cgw - tag: carsten + tag: next pullPolicy: Always # regcred: # registry: tip-tip-wlan-cloud-ucentral.jfrog.io @@ -19,41 +19,45 @@ services: cgw: type: ClusterIP ports: + # this port doesn't actually exist in cgw ... yet + # It is needed however, as the ALB requires at least one ssl port + restapi: + servicePort: 16002 + targetPort: 16002 + protocol: TCP websocket: servicePort: 15002 targetPort: 15002 protocol: TCP + metrics: + servicePort: 15003 + targetPort: 8080 + protocol: TCP grpc: servicePort: 15051 targetPort: 50051 protocol: TCP checks: - cgw: {} -# TODO: what should this be for CGW? -# liveness: -# httpGet: -# path: / -# port: 16102 -# readiness: -# exec: -# command: -# - /readiness_check + cgw: + liveness: + httpGet: + path: /health + port: 8080 + readiness: + httpGet: + path: /health + port: 8080 ingresses: {} volumes: cgw: - name: certs - mountPath: /cgw-data/certs + mountPath: /etc/cgw/certs volumeDefinition: | secret: secretName: {{ if .Values.existingCertsSecret }}{{ .Values.existingCertsSecret }}{{ else }}{{ include "cgw.fullname" . }}-certs{{ end }} - - name: persist - mountPath: /cgw-data/persist - volumeDefinition: | - persistentVolumeClaim: - claimName: {{ template "cgw.fullname" . }}-pvc resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -82,7 +86,7 @@ podSecurityPolicy: enabled: false persistence: - enabled: true + enabled: false # storageClassName: "-" accessModes: - ReadWriteOnce @@ -92,22 +96,21 @@ persistence: # Application public_env_variables: CGW_ROOT: /cgw-data - CGW_WSS_CAS: "/cgw-data/certs/cas.pem" - CGW_WSS_CERT: "/cgw-data/certs/websocket-cert.pem" - CGW_WSS_KEY: "/cgw-data/certs/websocket-key.pem" - CGW_DB_HOST: "psql" - CGW_DB_IP: "127.0.0.1" + CGW_WSS_CAS: "cas.pem" + CGW_WSS_CERT: "websocket-cert.pem" + CGW_WSS_KEY: "websocket-key.pem" + CGW_DB_HOST: "pgsql" CGW_DB_PORT: "5432" CGW_DB_NAME: "cgw" CGW_DB_USERNAME: "cgw" CGW_KAFKA_HOST: "kafka" - CGW_KAFKA_IP: "127.0.0.1" CGW_KAFKA_PORT: "9092" - CGW_REDIS_IP: "127.0.0.1" CGW_REDIS_HOST: "redis" - CGW_REDIS_PORT: "9092" + CGW_REDIS_PORT: "6379" + CFG_LOG_LEVEL: "Info" # or Debug CGW_KAFKA_CONSUME_TOPIC: "CnC" CGW_KAFKA_PRODUCE_TOPIC: "CnC_Res" + DEFAULT_WSS_THREAD_NUM: "4" secret_env_variables: CGW_DB_PASSWORD: "123" diff --git a/utils/client_simulator/.gitignore b/utils/client_simulator/.gitignore index 9c99192..62ad7e6 100644 --- a/utils/client_simulator/.gitignore +++ b/utils/client_simulator/.gitignore @@ -4,3 +4,5 @@ lib64 include/ *.cfg __pycache__ +tipbundle.tgz +tipcerts diff --git a/utils/client_simulator/Dockerfile b/utils/client_simulator/Dockerfile index 798514c..8f878e0 100644 --- a/utils/client_simulator/Dockerfile +++ b/utils/client_simulator/Dockerfile @@ -1,10 +1,4 @@ -FROM ubuntu - -COPY ./requirements.txt /tmp/requirements.txt +FROM python:3.12 WORKDIR /opt/client_simulator - -RUN apt-get update -q -y && apt-get -q -y --no-install-recommends install \ - python3 \ - python3-pip -RUN python3 -m pip install -r /tmp/requirements.txt - +COPY requirements.txt /tmp +RUN pip install -r /tmp/requirements.txt diff --git a/utils/client_simulator/runsingle b/utils/client_simulator/runsingle index b081674..611bb15 100755 --- a/utils/client_simulator/runsingle +++ b/utils/client_simulator/runsingle @@ -8,29 +8,10 @@ CA_CERT_PATH=$(pwd)/../cert_generator/certs/ca CA_CERT_PATH=./tipcerts CLIENT_CERT_PATH=$(pwd)/../cert_generator/certs/client CLIENT_CERT_PATH=./tipcerts +#--no-cert-check python3 single.py --mac "$MAC" \ --server "$URL" \ - --no-cert-check \ --ca-cert "$CA_CERT_PATH/ca.crt" \ --client-certs-path "$CLIENT_CERT_PATH" \ --msg-interval "$MSG_INTERVAL" \ --payload-size "$MSG_SIZE" -exit - - -MAC="11:22:AA:BB:CC:XX" -COUNT=20 -URL="wss://cgw-devcgw.cicd.lab.wlan.tip.build:15002" -MSG_INTERVAL=10 -MSG_SIZE=1000 -CA_CERT_PATH=$(pwd)/../cert_generator/certs/ca -#CA_CERT_PATH=./certs/ca -CLIENT_CERT_PATH=$(pwd)/../cert_generator/certs/client -#CLIENT_CERT_PATH=./certs/client -python3 main.py --mac ${MAC} -N ${COUNT} \ - --server ${URL} \ - --no-cert-check \ - --ca-cert $CA_CERT_PATH/ca.crt \ - --client-certs-path $CLIENT_CERT_PATH \ - --msg-interval ${MSG_INTERVAL} \ - --payload-size ${MSG_SIZE} diff --git a/utils/client_simulator/src/simulation_runner.py b/utils/client_simulator/src/simulation_runner.py index 0c82591..7ee7724 100644 --- a/utils/client_simulator/src/simulation_runner.py +++ b/utils/client_simulator/src/simulation_runner.py @@ -40,7 +40,7 @@ def from_json(msg) -> dict: class Device: def __init__(self, mac: str, server: str, ca_cert: str, msg_interval: int, msg_size: int, - client_cert: str, client_key: str, + client_cert: str, client_key: str, check_cert: bool, start_event: multiprocessing.Event, stop_event: multiprocessing.Event): self.mac = mac @@ -55,7 +55,11 @@ def __init__(self, mac: str, server: str, ca_cert: str, self.ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) self.ssl_context.load_cert_chain(client_cert, client_key, "") self.ssl_context.load_verify_locations(ca_cert) - self.ssl_context.verify_mode = ssl.CERT_REQUIRED + if check_cert: + self.ssl_context.verify_mode = ssl.CERT_REQUIRED + else: + self.ssl_context.check_hostname = False + self.ssl_context.verify_mode = ssl.CERT_NONE def send_hello(self, socket: client.ClientConnection): logger.debug(self.messages.connect) @@ -102,6 +106,25 @@ def disconnect(self): self._socket.close() self._socket = None + def single_run(self): + logger.debug("starting simulation") + self.connect() + start = time.time() + try: + self.send_hello(self._socket) + while True: + if self._socket is None: + logger.error("Connection to GW is lost. Trying to reconnect...") + self.connect() + if time.time() - start > self.interval: + logger.info(f"Sent log") + self.send_log(self._socket) + start = time.time() + self.handle_messages(self._socket) + finally: + self.disconnect() + logger.debug("simulation done") + def job(self): logger.debug("waiting for start trigger") self.start_event.wait() @@ -157,6 +180,7 @@ def process(args: Args, mask: str, start_event: multiprocessing.Event, stop_even devices = [Device(mac, args.server, args.ca_path, args.msg_interval, args.msg_size, os.path.join(args.cert_path, f"{mac}.crt"), os.path.join(args.cert_path, f"{mac}.key"), + args.check_cert, start_event, stop_event) for mac, _ in zip(macs, range(args.number_of_connections))] threads = [threading.Thread(target=d.job, name=d.mac) for d in devices] diff --git a/utils/client_simulator/src/utils.py b/utils/client_simulator/src/utils.py index 1204499..c6f44f4 100644 --- a/utils/client_simulator/src/utils.py +++ b/utils/client_simulator/src/utils.py @@ -22,6 +22,7 @@ class Args: server_proto: str = "ws" server_address: str = "localhost" server_port: int = 50001 + check_cert: bool = True @property def server(self): @@ -63,6 +64,9 @@ def parse_args(): parser.add_argument("-c", "--client-certs-path", metavar="PATH", default="./certs/client", help="path to client certificates directory") + parser.add_argument("-C", "--no-cert-check", type=bool, action='store_true', + default=False, + help="do not check certificate") parser.add_argument("-t", "--msg-interval", metavar="SECONDS", type=int, default=10, help="time between client messages to gw") @@ -80,13 +84,15 @@ def parse_args(): cert_path=parsed_args.client_certs_path, msg_interval=parsed_args.msg_interval, msg_size=parse_msg_size(parsed_args.payload_size), + check_certs=not parsed_args.no_cert_check, wait_for_sig=parsed_args.wait_for_signal) if len(args.masks) == 0: args.masks.append("XX:XX:XX:XX:XX:XX") # PROTO :// ADDRESS : PORT - match = re.match(r"(?:(wss?)://)?([\d\w\.]+):?(\d+)?", parsed_args.server) + # TODO: fixme the host portion can contain a lot more than just these characters! + match = re.match(r"(?:(wss?)://)?([\d\w\.-]+):?(\d+)?", parsed_args.server) if match is None: raise ValueError(f"Unable to parse server address {parsed_args.server}") proto, addr, port = match.groups() From dd6de62459f12e5b36634c5cbd5e22e76b0b3717 Mon Sep 17 00:00:00 2001 From: Carsten Schafer Date: Mon, 24 Jun 2024 15:15:54 -0400 Subject: [PATCH 4/5] Add toolbox --- utils/toolbox/Dockerfile | 52 ++++++++++++++++++++++++++++++++++++++++ utils/toolbox/buildit | 10 ++++++++ 2 files changed, 62 insertions(+) create mode 100644 utils/toolbox/Dockerfile create mode 100755 utils/toolbox/buildit diff --git a/utils/toolbox/Dockerfile b/utils/toolbox/Dockerfile new file mode 100644 index 0000000..904aab1 --- /dev/null +++ b/utils/toolbox/Dockerfile @@ -0,0 +1,52 @@ +FROM python:3.12 + +RUN apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + zip unzip kcat vim postgresql-client redis-tools perf-tools-unstable + +# client simulator +RUN mkdir -p client_simulator/src \ + client_simulator/data \ + client_simulator/certs \ + client_simulator/tipcerts +COPY client_simulator/main.py \ + client_simulator/single.py \ + client_simulator/runsingle \ + client_simulator/README.md \ + client_simulator/requirements.txt \ + client_simulator +COPY client_simulator/src/log.py \ + client_simulator/src/utils.py \ + client_simulator/src/simulation_runner.py \ + client_simulator/src/__init__.py \ + client_simulator/src +COPY client_simulator/data/message_templates.json \ + client_simulator/data +COPY client_simulator/certs/ca.crt \ + client_simulator/certs/ca.key \ + client_simulator/certs + +# kafka producer +RUN mkdir -p kafka_producer/src \ + kafka_producer/data +COPY kafka_producer/main.py \ + kafka_producer/requirements.txt \ + kafka_producer +COPY kafka_producer/src/log.py \ + kafka_producer/src/utils.py \ + kafka_producer/src/producer.py \ + kafka_producer/src/cli_parser.py \ + kafka_producer/src/__init__.py \ + kafka_producer/src +COPY kafka_producer/data/message_template.json \ + kafka_producer/data + +# cert generator +RUN mkdir -p cert_generator/certs/ca \ + cert_generator/certs/clients +COPY cert_generator/generate_certs.sh \ + cert_generator/ca.conf \ + cert_generator/README.md \ + cert_generator + +RUN pip install -r client_simulator/requirements.txt +RUN pip install -r kafka_producer/requirements.txt diff --git a/utils/toolbox/buildit b/utils/toolbox/buildit new file mode 100755 index 0000000..70b9f4f --- /dev/null +++ b/utils/toolbox/buildit @@ -0,0 +1,10 @@ +#!/bin/bash + +[ -z "$IMGTAG" ] && IMGTAG="latest" + +cd .. +docker build \ + -t tip-tip-wlan-cloud-ucentral.jfrog.io/cgw-toolbox:$IMGTAG \ + -f toolbox/Dockerfile . +docker push \ + tip-tip-wlan-cloud-ucentral.jfrog.io/cgw-toolbox:$IMGTAG From e86f807619e3673acd1c2e3c8524a0bce8d5c46d Mon Sep 17 00:00:00 2001 From: Carsten Schafer Date: Tue, 25 Jun 2024 10:13:42 -0400 Subject: [PATCH 5/5] Add ci.yml Signed-off-by: Carsten Schafer --- .github/workflows/ci.yml | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..324fee5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +name: CI + +on: + push: + paths-ignore: + - '**.md' + branches: + - next + - 'release/*' + tags: + - 'v*' + pull_request: + branches: + - next + - 'release/*' + +defaults: + run: + shell: bash + +jobs: + docker: + runs-on: ubuntu-latest + env: + DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io + DOCKER_REGISTRY_USERNAME: ucentral + steps: + - name: Checkout actions repo + uses: actions/checkout@v4 + with: + repository: Telecominfraproject/.github + path: github + + - name: Build and push Docker image + uses: ./github/composite-actions/docker-image-build + with: + image_name: cgw + registry: tip-tip-wlan-cloud-ucentral.jfrog.io + registry_user: ucentral + registry_password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + + - name: Notify on failure via Slack + if: failure() && github.ref == 'refs/heads/next' + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_USERNAME: GitHub Actions failure notifier + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_COLOR: "${{ job.status }}" + SLACK_ICON: https://raw.githubusercontent.com/quintessence/slack-icons/master/images/github-logo-slack-icon.png + SLACK_TITLE: Docker build failed for OWGW service + + trigger-deploy-to-dev: + runs-on: ubuntu-latest + #if: github.ref == 'refs/heads/next' + if: github.ref == 'zzzzzzz' + needs: + - docker + steps: + - name: Checkout actions repo + uses: actions/checkout@v4 + with: + repository: Telecominfraproject/.github + path: github + + - name: Trigger deployment of the latest version to dev instance and wait for result + uses: ./github/composite-actions/trigger-workflow-and-wait + with: + owner: Telecominfraproject + repo: wlan-testing + workflow: cgw-dev-deployment.yaml + token: ${{ secrets.WLAN_TESTING_PAT }} + ref: master + inputs: '{"just_component": "true"}'