From 88611643768f0d3007237d702d22c4b970670c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Ollivier?= Date: Thu, 18 Apr 2024 11:15:18 +0200 Subject: [PATCH 1/9] Replace cncf/cnf-testsuite by cnti-testcatalog/testsuite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Please see https://github.com/cncf/cnf-testsuite?tab=readme-ov-file Signed-off-by: Cédric Ollivier --- doc/ref_cert/RC2/chapters/chapter04.rst | 56 ++++++++++++------------- doc/ref_cert/RC2/chapters/chapter05.rst | 4 +- doc/ref_cert/RC2/conf.py | 7 ++-- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/doc/ref_cert/RC2/chapters/chapter04.rst b/doc/ref_cert/RC2/chapters/chapter04.rst index 733d62b..2de6f0f 100644 --- a/doc/ref_cert/RC2/chapters/chapter04.rst +++ b/doc/ref_cert/RC2/chapters/chapter04.rst @@ -88,76 +88,76 @@ for Kubernetes workloads. * - ra2.app.011 - Published helm chart: Helm charts of the CNF must be published into a helm registry and must not be used from local copies. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Should * - ra2.app.012 - Valid Helm chart: Helm charts of the CNF must be valid and should pass the helm lint validation. - `CNCF CNF Testsuite - `__ + `__ - Should * - ra2.app.013 - Rolling update: Rolling update of the CNF must be possible using Kubernetes deployments. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.014 - Rolling downgrade: Rolling downgrade of the CNF must be possible using Kubernetes deployments. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.015 - CNI compatibility: The CNF must use CNI compatible networking plugins. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.016 - Kubernetes API stability: The CNF must not use any Kubernetes alpha API-s. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.017 - CNF resiliency (node drain): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of a node drain and rescheduling occurs. - - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.018 - CNF resiliency (network latency): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of network latency up to 2000 ms occurs. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.019 - CNF resiliency (pod delete) CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod delete occurs. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (not) * - ra2.app.020 - CNF resiliency (pod memory hog): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod memory hog occurs. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.021 - CNF resiliency (pod I/O stress): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod I/O stress occurs. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.022 - CNF resiliency (pod network corruption): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod network corruption occurs. - - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.023 - CNF resiliency (pod network duplication): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod network duplication occurs. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.024 - CNF resiliency (pod DNS error): CNF must not lose data, must continue @@ -167,16 +167,16 @@ for Kubernetes workloads. - Must (Not) * - ra2.app.025 - CNF local storage: CNF must not use local storage. - - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.026 - Liveness probe: All Pods of the CNF must have livenessProbe defined. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.027 - Readiness probe: All Pods of the CNF must have readinessProbe defined. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.028 - No access to container daemon sockets: The CNF must not have any of the @@ -189,30 +189,30 @@ for Kubernetes workloads. must not be automatically mapped. To prevent this the automountServiceAccountToken: false flag must be set in all Pods of the CNF. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.030 - No host network access: Host network must not be attached to any of the Pods of the CNF. hostNetwork attribute of the Pod specifications must be False or should not be specified. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.031 - Host process namespace separation: Pods of the CNF must not share the host process ID namespace or the host IPC namespace. Pod manifests must not have the hostPID or the hostIPC attribute set to true. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.032 - Resource limits: All containers and namespaces of the CNF must have defined resource limits for at least CPU and memory resources. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.033 - Read only filesystem: All containers of the CNF must have a read only filesystem. The readOnlyRootFilesystem attribute of the Pods in the their securityContext should be set to true. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.034 - Container image tags: All referred container images in the Pod @@ -223,7 +223,7 @@ for Kubernetes workloads. * - ra2.app.035 - No hardcoded IP addresses: The CNF must not have any hardcoded IP addresses in its Pod specifications. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.036 - No node ports: Service declarations of the CNF must not contain @@ -244,32 +244,32 @@ for Kubernetes workloads. * - ra2.app.039 - CNF image size: The different container images of the CNF should not be bigger than 5GB. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Should (Not) * - ra2.app.040 - CNF startup time: Startup time of the Pods of a CNF should not be more than 60s where startup time is the time between starting the Pod until the readiness probe outcome is Success. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Should (Not) * - ra2.app.041 - Pods of the CNF must not run in privileged mode. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must (Not) * - ra2.app.042 - No root user: None of the Pods of the CNF should run as a root user. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Should (Not) * - ra2.app.043 - No privilege escalation: None of the containers of the CNF should allow privilege escalation. - - `CNCF CNF Testsuite `__ - Should (Not) * - ra2.app.044 - All the Pods of the CNF must be able to execute with a non-root user having a non-root group. Both the runAsUser and the runAsGroup attributes must be set to a value greater than 999. - - `CNCF CNF Testsuite `__ + - `CNCF CNF Testsuite `__ - Must * - ra2.app.045 - Labels: Pods of the CNF should define at least the following labels: diff --git a/doc/ref_cert/RC2/chapters/chapter05.rst b/doc/ref_cert/RC2/chapters/chapter05.rst index 78d4257..0edf090 100644 --- a/doc/ref_cert/RC2/chapters/chapter05.rst +++ b/doc/ref_cert/RC2/chapters/chapter05.rst @@ -4,10 +4,10 @@ The purpose of this chapter is to direct the reader to information regarding the test suites available and where to find the test suite code for workloads. The description of the tests can be found in the -`CNCF CNF Testsuite Descriptions `__. +`CNCF CNF Testsuite Descriptions `__. The installation of the CNCF test suite is described in the -`CNCF CNF Testsuite Installation `__. +`CNCF CNF Testsuite Installation `__. Some of the necessary tests are found in `Kubernetes documentation `__. diff --git a/doc/ref_cert/RC2/conf.py b/doc/ref_cert/RC2/conf.py index 7500b53..4732b0a 100644 --- a/doc/ref_cert/RC2/conf.py +++ b/doc/ref_cert/RC2/conf.py @@ -12,9 +12,8 @@ html_theme = "piccolo_theme" linkcheck_ignore = [ 'http://127.0.0.1', - 'https://github.com/cncf/cnf-testsuite/', - 'https://github.com/opencontainers/', - 'https://build.opnfv.org/' + 'https://build.opnfv.org/', + 'https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#' ] intersphinx_mapping = { 'ref_arch_kubernetes': ('https://cntt.readthedocs.io/projects/ra2/en/latest/', None) @@ -43,4 +42,4 @@ html_favicon = '_static/favicon.ico' bibtex_bibfiles = ['refs.bib'] -bibtex_default_style = 'unsrt' \ No newline at end of file +bibtex_default_style = 'unsrt' From 03c1cf2c3eb1086de3bb09057c7f67a098edc68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Ollivier?= Date: Thu, 18 Apr 2024 13:02:59 +0200 Subject: [PATCH 2/9] Update Kubernetes release to v1.29 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RA2 made the choice to support v1.29. Signed-off-by: Cédric Ollivier --- doc/ref_cert/RC2/chapters/chapter02.rst | 78 ++++++++++++------------- doc/ref_cert/RC2/chapters/chapter03.rst | 6 +- doc/ref_cert/RC2/conf.py | 1 - 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/doc/ref_cert/RC2/chapters/chapter02.rst b/doc/ref_cert/RC2/chapters/chapter02.rst index 315aedb..d0d5311 100644 --- a/doc/ref_cert/RC2/chapters/chapter02.rst +++ b/doc/ref_cert/RC2/chapters/chapter02.rst @@ -203,10 +203,10 @@ Network Testing The regexes load.balancer, LoadBalancer and Network.should.set.TCP.CLOSE_WAIT.timeout are currently skipped because -they haven’t been covered successfully neither by -`sig-release-1.25-blocking `__ +they haven't been covered successfully neither by +`sig-release-1.29-blocking `__ nor by `Anuket RC2 -verification `__ +verification `__ Please note that a couple of tests must be skipped by name below as they are no appropriate labels. @@ -285,7 +285,7 @@ Storage Testing It should be noted that all in-tree driver testing, [Driver:+], is skipped. Conforming to `the upstream -gate `__, +gate `__, all PersistentVolumes NFS testing is also skipped. The following exclusions are about `the deprecated in-tree GitRepo volume type `__: @@ -328,9 +328,9 @@ Kubernetes API benchmarking that performs Kubernetes API benchmarking. `Functest Kubernetes -Benchmarking `__ +Benchmarking `__ proposed a Rally-based test case, -`xrally_kubernetes_full `__, +`xrally_kubernetes_full `__, which iterates 10 times the mainline `xrally-kubernetes `__ scenarios. @@ -338,10 +338,10 @@ scenarios. At the time of writing, no KPI is defined in :doc:`ref_arch_kubernetes:index` which would have asked for an update of the default SLA (maximum failure rate of 0%) proposed in `Functest Kubernetes -Benchmarking `__ +Benchmarking `__ `Functest -xrally_kubernetes_full `__: +xrally_kubernetes_full `__: .. list-table:: Kubernetes API benchmarking :widths: 80 20 @@ -395,7 +395,7 @@ xrally_kubernetes_full `__. At the time of writing, no KPI is defined in Anuket chapters which would have asked for an update of the default SLA proposed in `Functest Kubernetes -Benchmarking `__. +Benchmarking `__. Security testing ~~~~~~~~~~~~~~~~ There are a couple of opensource tools that help securing the Kubernetes stack. Amongst them, `Functest Kubernetes -Security `__ +Security `__ offers two test cases based on `kube-hunter `__ and `kube-bench `__. @@ -488,10 +488,10 @@ rules are defined as mandatory (e.g., sec.std.001: The Cloud Operator it would have required an update of the default kube-bench behavior (all failures and warnings are only printed) as integrated in `Functest Kubernetes -Security `__. +Security `__. The following software versions are considered to verify Kubernetes -v1.25 (latest stable release) selected by Anuket: +v1.29 (latest stable release) selected by Anuket: .. list-table:: Software versions :widths: 50 50 @@ -500,7 +500,7 @@ v1.25 (latest stable release) selected by Anuket: * - Software - Version * - Functest - - v1.25 + - v1.29 * - kube-hunter - 0.6.8 * - kube-bench @@ -520,7 +520,7 @@ upstream tests (see `clearwater-live-test `__). The following software versions are considered to verify Kubernetes -v1.25 (latest stable release) selected by Anuket: +v1.29 (latest stable release) selected by Anuket: .. list-table:: Software versions :widths: 50 50 @@ -529,7 +529,7 @@ v1.25 (latest stable release) selected by Anuket: * - Software - Version * - Functest - - v1.25 + - v1.29 * - clearwater - release-130 * - Helm @@ -548,91 +548,91 @@ The following test case must pass as they are for Reference Conformance: - Test suite - Criteria - Requirements - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - xrally_kubernetes - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - k8s_conformance - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - k8s_conformance_serial - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_api_machinery - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_api_machinery_serial - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_apps - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_apps_serial - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_auth - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_cluster_lifecycle - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_instrumentation - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_network - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_node - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_scheduling_serial - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_storage - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-smoke:v1.25 + * - opnfv/functest-kubernetes-smoke:v1.29 - sig_storage_serial - PASS - Kubernetes API testing - * - opnfv/functest-kubernetes-security:v1.25 + * - opnfv/functest-kubernetes-security:v1.29 - kube_hunter - PASS - Security testing - * - opnfv/functest-kubernetes-security:v1.25 + * - opnfv/functest-kubernetes-security:v1.29 - kube_bench_master - PASS - Security testing - * - opnfv/functest-kubernetes-security:v1.25 + * - opnfv/functest-kubernetes-security:v1.29 - kube_bench_node - PASS - Security testing - * - opnfv/functest-kubernetes-benchmarking:v1.25 + * - opnfv/functest-kubernetes-benchmarking:v1.29 - xrally_kubernetes_full - PASS - Kubernetes API benchmarking - * - opnfv/functest-kubernetes-benchmarking:v1.25 + * - opnfv/functest-kubernetes-benchmarking:v1.29 - netperf - PASS - Dataplane benchmarking - * - opnfv/functest-kubernetes-cnf:v1.25 + * - opnfv/functest-kubernetes-cnf:v1.29 - k8s_vims - PASS - Opensource CNF onboarding and testing - * - opnfv/functest-kubernetes-cnf:v1.25 + * - opnfv/functest-kubernetes-cnf:v1.29 - helm_vims - PASS - Opensource CNF onboarding and testing diff --git a/doc/ref_cert/RC2/chapters/chapter03.rst b/doc/ref_cert/RC2/chapters/chapter03.rst index c9dd235..e6a380f 100644 --- a/doc/ref_cert/RC2/chapters/chapter03.rst +++ b/doc/ref_cert/RC2/chapters/chapter03.rst @@ -36,7 +36,7 @@ To deploy your own CI toolchain running Anuket Compliance: ansible-galaxy install collivier.xtesting ansible-galaxy collection install ansible.posix community.general community.grafana kubernetes.core community.docker community.postgresql git clone https://gerrit.opnfv.org/gerrit/functest-kubernetes functest-kubernetes-src - (cd functest-kubernetes-src && git checkout -b stable/v1.25 origin/stable/v1.25) + (cd functest-kubernetes-src && git checkout -b stable/v1.29 origin/stable/v1.29) ansible-playbook functest-kubernetes-src/ansible/site.cntt.yml Configure Kubernetes API testing @@ -51,13 +51,13 @@ cookbook: Run Kubernetes conformance suite ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Open http://127.0.0.1:8080/job/functest-kubernetes-v1.25-daily/ in a web +Open http://127.0.0.1:8080/job/functest-kubernetes-v1.29-daily/ in a web browser, login as admin/admin and click on “Build with Parameters” (keep the default values). If the System under test (SUT) is Anuket compliant, a link to the full archive containing all test results and artifacts will be printed in -functest-kubernetes-v1.25-zip’s console. Be free to download it and then +functest-kubernetes-v1.29-zip’s console. Be free to download it and then to send it to any reviewer committee. To clean your working dir: diff --git a/doc/ref_cert/RC2/conf.py b/doc/ref_cert/RC2/conf.py index 4732b0a..9e60d4b 100644 --- a/doc/ref_cert/RC2/conf.py +++ b/doc/ref_cert/RC2/conf.py @@ -12,7 +12,6 @@ html_theme = "piccolo_theme" linkcheck_ignore = [ 'http://127.0.0.1', - 'https://build.opnfv.org/', 'https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#' ] intersphinx_mapping = { From 1f4ddb1e7aa4692086af794f0b9768b0dd1a5e5e Mon Sep 17 00:00:00 2001 From: Gergely Csatari Date: Thu, 18 Apr 2024 15:29:39 +0300 Subject: [PATCH 3/9] Fixing the headers in README Signed-off-by: Gergely Csatari --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2225f5b..ff563ff 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ This repo contains the source for the Issues for this repo are handled in the [anuket-project/anuket-specifications](https://github.com/anuket-project/anuket-specifications/issues) repository. -# Pull Requests +## Pull Requests Pull requests are welcome in [this repository](https://github.com/anuket-project/RC2/pulls). For the contribution guide please follow [this](https://github.com/anuket-project/anuket-specifications/blob/master/CONTRIBUTING.rst) link. -# Terms of Reference & License +## Terms of Reference & License - [Terms of Reference](https://github.com/anuket-project/anuket-specifications/blob/master/doc/GSMA_CNTT_Terms_of_Reference.pdf) - [Code of Conduct](https://github.com/anuket-project/anuket-specifications/blob/master/doc/CODE_OF_CONDUCT.rst) From 810f4f2427e20e872d7a0a616ae2a82374d31bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Ollivier?= Date: Fri, 19 Apr 2024 14:34:21 +0200 Subject: [PATCH 4/9] Update the testcase descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It defact skips all alpha and beta features. See https://github.com/opnfv/functest-kubernetes/blame/stable/v1.29/docker/smoke/testcases.yaml Signed-off-by: Cédric Ollivier --- doc/ref_cert/RC2/chapters/chapter02.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/ref_cert/RC2/chapters/chapter02.rst b/doc/ref_cert/RC2/chapters/chapter02.rst index d0d5311..2cb980b 100644 --- a/doc/ref_cert/RC2/chapters/chapter02.rst +++ b/doc/ref_cert/RC2/chapters/chapter02.rst @@ -87,7 +87,7 @@ Testing `__ @@ -156,6 +157,7 @@ skip: - [Disruptive] - [Flaky] - [Feature:BoundServiceAccountTokenVolume] +- [Feature:ClusterTrustBundle] - [Feature:PodSecurityPolicy] See `Auth Special Interest @@ -231,6 +233,7 @@ skip: - [Feature:ProxyTerminatingEndpoints] - [Feature:SCTP] - [Feature:SCTPConnectivity] +- [Feature:ServiceCIDRs] - DNS configMap nameserver - load.balancer - LoadBalancer @@ -250,11 +253,18 @@ skip: - [alpha] - [Disruptive] - [Flaky] +- [Feature:DynamicResourceAllocation] - [Feature:ExperimentalResourceUsageTracking] - [Feature:GRPCContainerProbe] - [Feature:GPUUpgrade] +- [Feature:InPlacePodVerticalScaling] +- [Feature:KubeletCredentialProviders] +- [Feature:NodeLogQuery] - [Feature:PodGarbageCollector] +- [Feature:PodLifecycleSleepAction] - [Feature:RegularResourceUsageTracking] +- [Feature:SidecarContainers] +- [Feature:UserNamespacesSupport] - [Feature:UserNamespacesStatelessPodsSupport] - [NodeFeature:DownwardAPIHugePages] - [NodeFeature:RuntimeHandler] @@ -309,6 +319,8 @@ skip: - [Feature:GKELocalSSD] - [Feature:VolumeSnapshotDataSource] - [Feature:Flexvolumes] +- [Feature:RecoverVolumeExpansionFailure] +- [Feature:SELinux] - [Feature:vsphere] - [Feature:Volumes] - [Feature:Windows] From 680bd40165b6d58ceddedb9c03a39b212978e22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Ollivier?= Date: Fri, 19 Apr 2024 14:57:13 +0200 Subject: [PATCH 5/9] Update all dependency versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It updates both rally and kube-bench versions as in Functest Kubernetes v1.29. Signed-off-by: Cédric Ollivier --- doc/ref_cert/RC2/chapters/chapter02.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ref_cert/RC2/chapters/chapter02.rst b/doc/ref_cert/RC2/chapters/chapter02.rst index 2cb980b..3a9f146 100644 --- a/doc/ref_cert/RC2/chapters/chapter02.rst +++ b/doc/ref_cert/RC2/chapters/chapter02.rst @@ -418,7 +418,7 @@ v1.29 (latest stable release) selected by Anuket: * - Functest - v1.29 * - xrally-kubernetes - - 1.1.1.dev12 + - 1.1.1.dev14+g2ffa85a Dataplane benchmarking ~~~~~~~~~~~~~~~~~~~~~~ @@ -429,7 +429,7 @@ Kubernetes-related performance test related tools especially `netperf `__ which benchmarks Kubernetes networking performance. -As listed in `netperf’s +As listed in `netperf's README `__, the 5 major network traffic paths are combination of pod IP vs virtual IP and whether the pods are co-located on the same node versus a @@ -516,7 +516,7 @@ v1.29 (latest stable release) selected by Anuket: * - kube-hunter - 0.6.8 * - kube-bench - - v0.6.9 + - v0.6.10 Opensource CNF onboarding and testing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 2523b2321e92d5d1780afa70145eed1e1f6074c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Ollivier?= Date: Mon, 22 Apr 2024 15:28:41 +0200 Subject: [PATCH 6/9] Pin sphinxcontrib-applehelp and its dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Else doc cannot be built on readthedocs as latest sphinxcontrib-applehelp versions ask for Sphinx>=5.0. Signed-off-by: Cédric Ollivier --- doc/ref_cert/RC2/test-requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/ref_cert/RC2/test-requirements.txt b/doc/ref_cert/RC2/test-requirements.txt index 5d45494..2dd31b2 100644 --- a/doc/ref_cert/RC2/test-requirements.txt +++ b/doc/ref_cert/RC2/test-requirements.txt @@ -4,3 +4,8 @@ sphinx==4.5.0 # BSD doc8==0.11.2 # Apache-2.0 # Apache-2.0 piccolo-theme==0.16.0 # MIT +sphinxcontrib-devhelp===1.0.2 +sphinxcontrib-applehelp===1.0.2 +sphinxcontrib-htmlhelp===2.0.0 +sphinxcontrib-qthelp===1.0.3 +sphinxcontrib-serializinghtml===1.1.5 From 039cb04478971da198948d1d3d4ecf260e37cce1 Mon Sep 17 00:00:00 2001 From: Gergely Csatari Date: Thu, 23 May 2024 14:03:59 +0300 Subject: [PATCH 7/9] Moving references to refs.bib Signed-off-by: Gergely Csatari --- doc/ref_cert/RC2/chapters/chapter01.rst | 7 +++ doc/ref_cert/RC2/chapters/chapter02.rst | 67 +++++++++++-------------- doc/ref_cert/RC2/conf.py | 3 +- doc/ref_cert/RC2/test-requirements.txt | 1 + doc/ref_cert/RC2/tox.ini | 3 +- 5 files changed, 40 insertions(+), 41 deletions(-) diff --git a/doc/ref_cert/RC2/chapters/chapter01.rst b/doc/ref_cert/RC2/chapters/chapter01.rst index 0d5d19e..e493931 100644 --- a/doc/ref_cert/RC2/chapters/chapter01.rst +++ b/doc/ref_cert/RC2/chapters/chapter01.rst @@ -190,3 +190,10 @@ infrastructure. Similarly, Chapter 4 maps test cases map to requirements for CNFs and Chapter 5 builds a testing cookbook. Chapter 6 encompasses any gaps in the Reference Conformance 2. + +References +---------- + +.. bibliography:: + :cited: + diff --git a/doc/ref_cert/RC2/chapters/chapter02.rst b/doc/ref_cert/RC2/chapters/chapter02.rst index 315aedb..f83ac72 100644 --- a/doc/ref_cert/RC2/chapters/chapter02.rst +++ b/doc/ref_cert/RC2/chapters/chapter02.rst @@ -52,36 +52,31 @@ Traceability Matrix Kubernetes API testing ~~~~~~~~~~~~~~~~~~~~~~ -The primary objectives of the `e2e -tests `__ -are to ensure a consistent and reliable behavior of the Kubernetes code -base, and to catch hard-to-test bugs before users do, when unit and -integration tests are insufficient. They are partially selected for the -`Software Conformance Certification -program `__ run by the +The primary objectives of the e2e tests :cite:p:`rc2-kubernetes-e2e-tests` are +to ensure a consistent and reliable behavior of the Kubernetes code base, and +to catch hard-to-test bugs before users do, when unit and integration tests are +insufficient. They are partially selected for the Software Conformance +Certification program :cite:p:`rc2-kubernetes-conformance` run by the Kubernetes community (under the aegis of the CNCF). Anuket shares the same goal to give end users the confidence that when they use a certified product they can rely on a high level of common -functionality. Then Anuket RC2 starts with the test list defined by `K8s -Conformance `__ which is +functionality. Then Anuket RC2 starts with the test list defined by K8s +Conformance :cite:p:`rc2-kubernetes-conformance` which is expected to grow according to the ongoing requirement traceability. -`End-to-End -Testing `__ -basically asks for focus and skip regexes to select or to exclude +End-to-End Testing :cite:p:`rc2-kubernetes-e2e-tests` basically asks for focus +and skip regexes to select or to exclude single tests: -- focus basically matches Conformance or `Testing Special Interest - Groups `__ - in sub-sections below -- skip excludes the SIG labels listed as optional in - :doc:`ref_arch_kubernetes:chapters/chapter06`. +- focus basically matches Conformance or Testing Special Interest Groups + :cite:p:`rc2-kubernetes-sig-testing` in sub-sections below +- skip excludes the SIG labels listed as optional in Chapter 6 of + :cite:t:`rc2-ra2`. The Reference Conformance suites must be stable and executed on real deployments. Then all the following labels are defacto skipped in -`End-to-End -Testing `__: +End-to-End Testing :cite:p:`rc2-kubernetes-e2e-tests`: - alpha - Disruptive @@ -93,11 +88,10 @@ Conformance as per the rules. Conformance ^^^^^^^^^^^ -It must be noted that the default `K8s -Conformance `__ testing is -disruptive thus Anuket RC2 rather picks -`non-disruptive-conformance `__ -testing as defined by `Sonobuoy `__. +It must be noted that the default K8s Conformance +:cite:p:`rc2-kubernetes-conformance` testing is disruptive thus Anuket RC2 +rather picks non-disruptive-conformance :cite:p:`rc2-sonobuoy-e2eplugin` +testing as defined by Sonobuoy :cite:p:`rc2-sonobuoy`. focus: `Conformance <#conformance>`__ @@ -120,9 +114,8 @@ skip: - [Feature:CustomResourceValidationExpressions] - [Feature:StorageVersionAPI] -See `API Machinery Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06` for more details. +See :cite:t:`rc2-k8s-api-sig-api-machinery` and Chapter 6 of :cite:t:`rc2-ra2` +for more details. Apps Testing ^^^^^^^^^^^^ @@ -141,9 +134,8 @@ skip: - [Feature:StatefulUpgrade] - [Feature:SuspendJob] -See `Apps Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06` for more details. +See :cite:t:`rc2-k8s-api-sig-apps` and Chapter 6 of :cite:t:`rc2-ra2` for more +details. Auth Testing ^^^^^^^^^^^^ @@ -158,9 +150,8 @@ skip: - [Feature:BoundServiceAccountTokenVolume] - [Feature:PodSecurityPolicy] -See `Auth Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06` for more details. +See :cite:t:`rc2-k8s-api-sig-auth` and Chapter 6 of :cite:t:`rc2-ra2` for more +details. Cluster Lifecycle Testing ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -173,9 +164,8 @@ skip: - [Disruptive] - [Flaky] -See `Cluster Lifecycle Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06` for more details. +See :cite:t:`rc2-k8s-api-sig-cluster-lifecycle` and Chapter 6 of +:cite:t:`rc2-ra2` for more details. Instrumentation Testing ^^^^^^^^^^^^^^^^^^^^^^^ @@ -194,9 +184,8 @@ skip: - [Feature:StackdriverMetadataAgent] - [Feature:StackdriverMonitoring] -See `Instrumentation Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06` for more details. +See :cite:t:`rc2-k8s-api-sig-instrumentation` and Chapter 6 of +:cite:t:`rc2-ra2` for more details. Network Testing ^^^^^^^^^^^^^^^ diff --git a/doc/ref_cert/RC2/conf.py b/doc/ref_cert/RC2/conf.py index 7500b53..8bc5e2c 100644 --- a/doc/ref_cert/RC2/conf.py +++ b/doc/ref_cert/RC2/conf.py @@ -7,7 +7,8 @@ ] extensions = [ 'sphinx.ext.intersphinx', - 'sphinx.ext.autosectionlabel' + 'sphinx.ext.autosectionlabel', + 'sphinxcontrib.bibtex' ] html_theme = "piccolo_theme" linkcheck_ignore = [ diff --git a/doc/ref_cert/RC2/test-requirements.txt b/doc/ref_cert/RC2/test-requirements.txt index 5d45494..6b384fa 100644 --- a/doc/ref_cert/RC2/test-requirements.txt +++ b/doc/ref_cert/RC2/test-requirements.txt @@ -4,3 +4,4 @@ sphinx==4.5.0 # BSD doc8==0.11.2 # Apache-2.0 # Apache-2.0 piccolo-theme==0.16.0 # MIT +sphinxcontrib-bibtex==2.5.0 diff --git a/doc/ref_cert/RC2/tox.ini b/doc/ref_cert/RC2/tox.ini index 7e40327..2e4260e 100644 --- a/doc/ref_cert/RC2/tox.ini +++ b/doc/ref_cert/RC2/tox.ini @@ -5,7 +5,7 @@ skipsdist = True [testenv:docs] basepython = python3.10 deps = - -chttps://opendev.org/openstack/requirements/raw/branch/stable/zed/upper-constraints.txt + -chttps://opendev.org/openstack/requirements/raw/branch/stable/2023.1/upper-constraints.txt -r{toxinidir}/test-requirements.txt install_command = pip install {opts} {packages} commands = @@ -13,3 +13,4 @@ commands = sphinx-build --keep-going -W -b html . build sphinx-build --keep-going -W -b latex . build sphinx-build --keep-going -W -b linkcheck . build + From ba3c38235c90b6a013dfee4040abebc9cd68e8a1 Mon Sep 17 00:00:00 2001 From: Gergely Csatari Date: Thu, 23 May 2024 16:32:32 +0300 Subject: [PATCH 8/9] Converting all external references to bibs file references Signed-off-by: Gergely Csatari --- doc/ref_cert/RC2/chapters/chapter02.rst | 131 ++++---- doc/ref_cert/RC2/chapters/chapter03.rst | 10 +- doc/ref_cert/RC2/chapters/chapter04.rst | 120 ++++---- doc/ref_cert/RC2/chapters/chapter05.rst | 10 +- doc/ref_cert/RC2/conf.py | 3 +- doc/ref_cert/RC2/refs-rc2.bib | 387 ++++++++++++++++++++++++ 6 files changed, 510 insertions(+), 151 deletions(-) create mode 100644 doc/ref_cert/RC2/refs-rc2.bib diff --git a/doc/ref_cert/RC2/chapters/chapter02.rst b/doc/ref_cert/RC2/chapters/chapter02.rst index 923dc48..067d72d 100644 --- a/doc/ref_cert/RC2/chapters/chapter02.rst +++ b/doc/ref_cert/RC2/chapters/chapter02.rst @@ -195,9 +195,8 @@ Network Testing The regexes load.balancer, LoadBalancer and Network.should.set.TCP.CLOSE_WAIT.timeout are currently skipped because they haven't been covered successfully neither by -`sig-release-1.29-blocking `__ -nor by `Anuket RC2 -verification `__ +:cite:t:`rc2-sig-release-1.29-blocking` nor by Anuket RC2 Verification +:cite:p:`rc2-functest-k8s-jobs-1.29` Please note that a couple of tests must be skipped by name below as they are no appropriate labels. @@ -228,9 +227,7 @@ skip: - LoadBalancer - Network.should.set.TCP.CLOSE_WAIT.timeout -See `Network Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06`. +See :cite:t:`rc2-k8s-api-sig-network` and Chapter 6 of :cite:t:`rc2-ra2`. Node Testing ^^^^^^^^^^^^ @@ -258,9 +255,7 @@ skip: - [NodeFeature:DownwardAPIHugePages] - [NodeFeature:RuntimeHandler] -See `Node Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06`. +See :cite:t:`rc2-k8s-api-sig-node` and Chapter 6 of :cite:t:`rc2-ra2`. Scheduling Testing ^^^^^^^^^^^^^^^^^^ @@ -275,19 +270,16 @@ skip: - [Feature:GPUDevicePlugin] - [Feature:Recreate] -See `Scheduling Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06`. +See :cite:t:`rc2-k8s-api-sig-scheduling` and Chapter 6 of :cite:t:`rc2-ra2`. Storage Testing ^^^^^^^^^^^^^^^ It should be noted that all in-tree driver testing, [Driver:+], is -skipped. Conforming to `the upstream -gate `__, -all PersistentVolumes NFS testing is also skipped. The following -exclusions are about `the deprecated in-tree GitRepo volume -type `__: +skipped. Conforming to the upstream gate +:cite:p:`rc2-sig-release-1.29-blocking`, all PersistentVolumes NFS testing is +also skipped. The following exclusions are about the deprecated in-tree GitRepo +volume type :cite:p:`rc2-k8s-kind-sig-issue-2356`: - should provision storage with different parameters - should not cause race condition when used for git_repo @@ -318,31 +310,24 @@ skip: - should provision storage with different parameters - should not cause race condition when used for git_repo -See `Storage Special Interest -Group `__ -and :doc:`ref_arch_kubernetes:chapters/chapter06`. +See :cite:t:`rc2-k8s-api-sig-storage` and Chapter 6 of :cite:t:`rc2-ra2`. Kubernetes API benchmarking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`Rally `__ is a tool and framework -that performs Kubernetes API benchmarking. +:cite:t:`rc2-openstack-rally` is a tool and framework that performs Kubernetes +API benchmarking. -`Functest Kubernetes -Benchmarking `__ -proposed a Rally-based test case, -`xrally_kubernetes_full `__, -which iterates 10 times the mainline -`xrally-kubernetes `__ -scenarios. +Functest Kubernetes Benchmarking :cite:p:`rc2-functest-kubernetes-benchmarking` +proposed a Rally-based test case, xrally_kubernetes_full +:cite:p:`rc2-xrally-kubernetes-full`, which iterates 10 times the mainline +:cite:p:`rc2-xrally-kubernetes` scenarios. -At the time of writing, no KPI is defined in :doc:`ref_arch_kubernetes:index` -which would have asked for an update of the default SLA (maximum failure -rate of 0%) proposed in `Functest Kubernetes -Benchmarking `__ +At the time of writing, no KPI is defined in :cite:t:`rc2-ra2` which would have +asked for an update of the default SLA (maximum failure rate of 0%) proposed in +Functest Kubernetes Benchmarking :cite:p:`rc2-functest-kubernetes-benchmarking` -`Functest -xrally_kubernetes_full `__: +Functest xrally_kubernetes_full :cite:p:`rc2-xrally-kubernetes-full`: .. list-table:: Kubernetes API benchmarking :widths: 80 20 @@ -412,17 +397,13 @@ v1.29 (latest stable release) selected by Anuket: Dataplane benchmarking ~~~~~~~~~~~~~~~~~~~~~~ -`Kubernetes perf-tests -repository `__ hosts various -Kubernetes-related performance test related tools especially -`netperf `__ -which benchmarks Kubernetes networking performance. +Kubernetes perf-tests repository :cite:p:`rc2-k8s-perf-tests` hosts various +Kubernetes-related performance test related tools especially netperf +:cite:p:`rc2-k8s-netperf` which benchmarks Kubernetes networking performance. -As listed in `netperf's -README `__, -the 5 major network traffic paths are combination of pod IP vs virtual -IP and whether the pods are co-located on the same node versus a -remotely located pod: +As listed in netperf's README :cite:p:`rc2-k8s-netperf`, the 5 major network +traffic paths are combination of pod IP vs virtual IP and whether the pods are +co-located on the same node versus a remotely located pod: - same node using pod IP - same node using cluster/virtual IP @@ -430,41 +411,33 @@ remotely located pod: - remote node using cluster/virtual IP - same node pod hairpin to itself using cluster/virtual IP -It should be noted that -`netperf `__ -leverages `iperf `__ (both TCP and UDP -modes) and `Netperf `__. +It should be noted that netperf :cite:p:`rc2-k8s-netperf` leverages iperf +:cite:p:`rc2-iperf` (both TCP and UDP modes) and Netperf :cite:p:`rc2-netperf`. At the time of writing, no KPI is defined in Anuket chapters which would -have asked for an update of the default SLA proposed in `Functest -Kubernetes -Benchmarking `__. +have asked for an update of the default SLA proposed in Functest Kubernetes +Benchmarking :cite:p:`rc2-functest-kubernetes-benchmarking`. Security testing ~~~~~~~~~~~~~~~~ There are a couple of opensource tools that help securing the Kubernetes -stack. Amongst them, `Functest Kubernetes -Security `__ -offers two test cases based on -`kube-hunter `__ and -`kube-bench `__. - -`kube-hunter `__ hunts for -security weaknesses in Kubernetes clusters and -`kube-bench `__ checks -whether Kubernetes is deployed securely by running the checks documented -in the `CIS Kubernetes -Benchmark `__. - -`kube-hunter `__ classifies -all vulnerabilities as low, medium, and high. In context of this -conformance suite, all vulnerabilities are only printed for information. - -Here are the `vulnerability -categories `__ -tagged as high by -`kube-hunter `__: +stack. Amongst them, Functest Kubernetes Security +:cite:p:`rc2-functest-kubernetes-security` offers two test cases based on +kube-hunter :cite:p:`rc2-kube-hunter` and kube-bench :cite:p:`rc2-kube-bench`. + +kube-hunter :cite:p:`rc2-kube-hunter` hunts for security weaknesses in +Kubernetes clusters and kube-bench :cite:p:`rc2-kube-bench` checks whether +Kubernetes is deployed securely by running the checks documented in the +CIS Kubernetes Benchmark :cite:p:`rc2-cis-kubernetes-benchmark`. + +kube-hunter :cite:p:`rc2-kube-hunter` classifies all vulnerabilities as low, +medium, and high. In context of this conformance suite, all vulnerabilities are +only printed for information. + +Here are the vulnerability categories +:cite:p:`rc2-kube-hunter-vulnerability-categories` tagged as high by +kube-hunter :cite:p:`rc2-kube-hunter`: - ExposedSensitiveInterfacesTechnique - MountServicePrincipalTechnique @@ -487,9 +460,8 @@ At the time of writing, none of the Center for Internet Security (CIS) rules are defined as mandatory (e.g., sec.std.001: The Cloud Operator **should** comply with Center for Internet Security CIS Controls) else it would have required an update of the default kube-bench behavior (all -failures and warnings are only printed) as integrated in `Functest -Kubernetes -Security `__. +failures and warnings are only printed) as integrated in Functest Kubernetes +Security :cite:p:`rc2-functest-kubernetes-security`. The following software versions are considered to verify Kubernetes v1.29 (latest stable release) selected by Anuket: @@ -515,10 +487,9 @@ technical solution to ensure that the platforms meet Network Functions Virtualization requirements. Functest CNF offers 2 test cases which automatically onboard and test -`Clearwater IMS `__ via -kubectl and Helm. It’s worth mentioning that this CNF is covered by the -upstream tests (see -`clearwater-live-test `__). +Clearwater IMS :cite:p:`rc2-clearwater-ims` via kubectl and Helm. It’s worth +mentioning that this CNF is covered by the upstream tests (see +clearwater-live-test :cite:p:`rc2-clearwater-live-test`). The following software versions are considered to verify Kubernetes v1.29 (latest stable release) selected by Anuket: diff --git a/doc/ref_cert/RC2/chapters/chapter03.rst b/doc/ref_cert/RC2/chapters/chapter03.rst index e6a380f..bfe07bc 100644 --- a/doc/ref_cert/RC2/chapters/chapter03.rst +++ b/doc/ref_cert/RC2/chapters/chapter03.rst @@ -9,10 +9,10 @@ and only runs the containers selected by Anuket RC2. It will be completed by the next Anuket mandatory test cases and then a new CI description file will be proposed in a shared tree. -`Xtesting CI `__ only -requires internet access, GNU/Linux as Operating System and asks for a -few dependencies as described in `Deploy your own Xtesting CI/CD -toolchains `__: +Xtesting CI :cite:p:`rc2-ansible-xtesting-ci` only requires internet access, +GNU/Linux as Operating System and asks for a few dependencies as described in +Deploy your own Xtesting CI/CD toolchains +:cite:p:`rc2-ansible-role-xtesting-ci`: - python-virtualenv - git @@ -24,7 +24,7 @@ and the network settings: virtualenv (“Aborting, target uses selinux but python bindings (libselinux-python) aren’t installed!”) - Proxy: you may set your proxy in env for Ansible and in systemd for - Docker https://docs.docker.com/config/daemon/systemd/#httphttps-proxy + Docker :cite:p:`rc2-docker-proxy` To deploy your own CI toolchain running Anuket Compliance: diff --git a/doc/ref_cert/RC2/chapters/chapter04.rst b/doc/ref_cert/RC2/chapters/chapter04.rst index 2de6f0f..2cd2b9c 100644 --- a/doc/ref_cert/RC2/chapters/chapter04.rst +++ b/doc/ref_cert/RC2/chapters/chapter04.rst @@ -5,7 +5,7 @@ Introduction ------------ The scope of this chapter is to identify and list test cases based on -requirements defined in :doc:`ref_arch_kubernetes:index`. +requirements defined in :cite:t:`rc2-ra2`. This will serve as traceability between test cases and requirements for Kubernetes platform interoperability. @@ -38,9 +38,8 @@ configuration, deployment, runtime. Test Case Traceability to RA2 Requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This section focuses on the test cases covering the requirements in -:ref:`ref_arch_kubernetes:chapters/chapter04:kubernetes workloads` -for Kubernetes workloads. +This section focuses on the test cases covering the requirements in Chapter 4 +of :cite:t:`rc2-ra2` for Kubernetes workloads. .. list-table:: Traceability to RA2 Requirements :widths: 35 35 30 30 @@ -59,9 +58,10 @@ for Kubernetes workloads. - :ref:`int.api.01 ` - Must * - ra2.app.007 - - Workloads must not use `hostPath volumes `__, as - Pods with identical configuration (such as those created from a PodTemplate) may behave differently on different - nodes due to different files on the nodes. + - Workloads must not use hostPath volumes + :cite:p:`rc2-r8s-doc-hostpath-volume`, as Pods with identical + configuration (such as those created from a PodTemplate) may behave + differently on different nodes due to different files on the nodes. - :ref:`kcm.gen.02 ` - Must * - ra2.app.008 @@ -81,83 +81,79 @@ for Kubernetes workloads. * - ra2.app.010 - Node Feature Discovery (NFD) - Workload descriptors must use the labels advertised by - `Node Feature Discovery - `__ - to indicate which node software of hardware features they need. + Node Feature Discovery :cite:p:`rc2-k8s-nfd` to indicate which node + software of hardware features they need. - Must * - ra2.app.011 - Published helm chart: Helm charts of the CNF must be published into a helm registry and must not be used from local copies. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-helm_chart_published` - Should * - ra2.app.012 - Valid Helm chart: Helm charts of the CNF must be valid and should pass the helm lint validation. - - `CNCF CNF Testsuite - `__ + - :cite:t:`rc2-cnti-testsuite-helm_chart_valid` - Should * - ra2.app.013 - Rolling update: Rolling update of the CNF must be possible using Kubernetes deployments. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-rolling_update` - Must * - ra2.app.014 - Rolling downgrade: Rolling downgrade of the CNF must be possible using Kubernetes deployments. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-rolling_downgrade` - Must * - ra2.app.015 - CNI compatibility: The CNF must use CNI compatible networking plugins. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-cni_compatibility` - Must * - ra2.app.016 - Kubernetes API stability: The CNF must not use any Kubernetes alpha API-s. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-alpha_k8s_apis` - Must (Not) * - ra2.app.017 - CNF resiliency (node drain): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of a node drain and rescheduling occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-node_drain` - Must (Not) * - ra2.app.018 - CNF resiliency (network latency): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of network latency up to 2000 ms occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-pod_network_latency` - Must (Not) * - ra2.app.019 - CNF resiliency (pod delete) CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod delete occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-pod_delete` - Must (not) * - ra2.app.020 - CNF resiliency (pod memory hog): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod memory hog occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-pod_memory_hog` - Must (Not) * - ra2.app.021 - CNF resiliency (pod I/O stress): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod I/O stress occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-pod_io_stress` - Must (Not) * - ra2.app.022 - CNF resiliency (pod network corruption): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod network corruption occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-pod_network_corruption` - Must (Not) * - ra2.app.023 - CNF resiliency (pod network duplication): CNF must not loose data, must continue to run and its readiness probe outcome must be Success even in case of pod network duplication occurs. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-pod_network_duplication` - Must (Not) * - ra2.app.024 - CNF resiliency (pod DNS error): CNF must not lose data, must continue @@ -167,16 +163,15 @@ for Kubernetes workloads. - Must (Not) * - ra2.app.025 - CNF local storage: CNF must not use local storage. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-no_local_volume_configuration` - Must (Not) * - ra2.app.026 - Liveness probe: All Pods of the CNF must have livenessProbe defined. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-liveness` - Must * - ra2.app.027 - Readiness probe: All Pods of the CNF must have readinessProbe defined. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-readiness` - Must * - ra2.app.028 - No access to container daemon sockets: The CNF must not have any of the @@ -189,30 +184,30 @@ for Kubernetes workloads. must not be automatically mapped. To prevent this the automountServiceAccountToken: false flag must be set in all Pods of the CNF. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-service_account_mapping` - Must (Not) * - ra2.app.030 - No host network access: Host network must not be attached to any of the Pods of the CNF. hostNetwork attribute of the Pod specifications must be False or should not be specified. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-host_network` - Must (Not) * - ra2.app.031 - Host process namespace separation: Pods of the CNF must not share the host process ID namespace or the host IPC namespace. Pod manifests must not have the hostPID or the hostIPC attribute set to true. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-host_pid_ipc_privileges` - Must (Not) * - ra2.app.032 - Resource limits: All containers and namespaces of the CNF must have defined resource limits for at least CPU and memory resources. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-resource_policies` - Must * - ra2.app.033 - Read only filesystem: All containers of the CNF must have a read only filesystem. The readOnlyRootFilesystem attribute of the Pods in the their securityContext should be set to true. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-immutable_file_systems` - Must * - ra2.app.034 - Container image tags: All referred container images in the Pod @@ -223,75 +218,80 @@ for Kubernetes workloads. * - ra2.app.035 - No hardcoded IP addresses: The CNF must not have any hardcoded IP addresses in its Pod specifications. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-hardcoded_ip_addresses_in_k8s_runtime_configuration` - Must (Not) * - ra2.app.036 - No node ports: Service declarations of the CNF must not contain nodePort definition. - - `Kubernetes documentation `__ + - Kubernetes documentation, :cite:t:`rc2-k8s-service` - Must (Not) * - ra2.app.037 - Immutable config maps: ConfigMaps used by the CNF must be immutable. - - `Kubernetes documentation `__ + - Kubernetes documentation, :cite:t:`rc2-k8s-configmap-immutable` - Must * - ra2.app.038 - - If the CNF supports scaling, increasing and decreasing its capacity must be implemented using horizontal scaling. - If horizontal scaling is supported, automatic scaling must be possible using Kubernetes Horizontal Pod Autoscale - `Horizontal Pod Autoscale (HPA) `__ + - If the CNF supports scaling, increasing and decreasing its capacity must + be implemented using horizontal scaling. If horizontal scaling is + supported, automatic scaling must be possible using Kubernetes + Horizontal Pod Autoscale (HPA) :cite:t:`rc2-k8s-hpa` feature. - TBD - Must * - ra2.app.039 - CNF image size: The different container images of the CNF should not be bigger than 5GB. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-reasonable_image_size` - Should (Not) * - ra2.app.040 - CNF startup time: Startup time of the Pods of a CNF should not be more than 60s where startup time is the time between starting the Pod until the readiness probe outcome is Success. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-reasonable_startup_time` - Should (Not) * - ra2.app.041 - Pods of the CNF must not run in privileged mode. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-privileged_containers` - Must (Not) * - ra2.app.042 - No root user: None of the Pods of the CNF should run as a root user. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-non_root_user` - Should (Not) * - ra2.app.043 - No privilege escalation: None of the containers of the CNF should allow privilege escalation. - - `CNCF CNF Testsuite `__ + - :cite:t:`rc2-cnti-testsuite-privilege_escalation` - Should (Not) * - ra2.app.044 - - All the Pods of the CNF must be able to execute with a non-root user having a non-root group. Both the - runAsUser and the runAsGroup attributes must be set to a value greater than 999. - - `CNCF CNF Testsuite `__ + - All the Pods of the CNF must be able to execute with a non-root user + having a non-root group. Both the runAsUser and the runAsGroup + attributes must be set to a value greater than 999. + - :cite:t:`rc2-cnti-testsuite-non_root_containers` - Must * - ra2.app.045 - Labels: Pods of the CNF should define at least the following labels: app.kubernetes.io/name, app.kubernetes.io/version and app.kubernetes.io/part-of - - `Kubernetes documentation `__ + - :cite:t:`rc2-k8s-recommended-labels` - Should * - ra2.app.046 - - The Pods of the CNF must direct their logs to sdout or stderr. This enables the treatment of the logs as event - steams. - - `Kubernetes documentation `__ + - The Pods of the CNF must direct their logs to sdout or stderr. This + enables the treatment of the logs as event steams. + - :cite:t:`rc2-k8s-recommended-labels` - Must * - ra2.app.047 - - The Pods of the CNF should not use the host ports. Using the host ports ties the CNF to a specific node, thereby - making the CNF less portable and scalable. + - The Pods of the CNF should not use the host ports. Using the host ports + ties the CNF to a specific node, thereby making the CNF less portable + and scalable. - - Must * - ra2.app.048 - - If SELinux is used in the Pods of the CNF, the options used to escalate privileges should not be allowed. The - options spec.securityContext.seLinuxOptions.type, spec.containers[*].securityContext.seLinuxOptions.type, + - If SELinux is used in the Pods of the CNF, the options used to escalate + privileges should not be allowed. The options + spec.securityContext.seLinuxOptions.type, + spec.containers[*].securityContext.seLinuxOptions.type, spec.initContainers[*].securityContext.seLinuxOptions, and - spec.ephemeralContainers[*].securityContext.seLinuxOptions.type must either be unset altogether or set to one of - the following allowed values container_t, container_init_t, or container_kvm_t. + spec.ephemeralContainers[*].securityContext.seLinuxOptions.type must + either be unset altogether or set to one of the following allowed values + container_t, container_init_t, or container_kvm_t. - - Must diff --git a/doc/ref_cert/RC2/chapters/chapter05.rst b/doc/ref_cert/RC2/chapters/chapter05.rst index 0edf090..2770304 100644 --- a/doc/ref_cert/RC2/chapters/chapter05.rst +++ b/doc/ref_cert/RC2/chapters/chapter05.rst @@ -4,13 +4,13 @@ The purpose of this chapter is to direct the reader to information regarding the test suites available and where to find the test suite code for workloads. The description of the tests can be found in the -`CNCF CNF Testsuite Descriptions `__. +CNTI CNF Testsuite Descriptions :cite:p:`rc2-cnti-testsuite-list-of-tests`. -The installation of the CNCF test suite is described in the -`CNCF CNF Testsuite Installation `__. +The installation of the CNCF test suite is described in the CNTI CNF Testsuite +Installation :cite:p:`rc2-cnti-testsuite-install`. -Some of the necessary tests are found in -`Kubernetes documentation `__. +Some of the necessary tests are found in Kubernetes documentation +:cite:p:`rc2-k8s-recommended-labels`. Functest ======== diff --git a/doc/ref_cert/RC2/conf.py b/doc/ref_cert/RC2/conf.py index 29bad4e..8b262f6 100644 --- a/doc/ref_cert/RC2/conf.py +++ b/doc/ref_cert/RC2/conf.py @@ -13,6 +13,7 @@ html_theme = "piccolo_theme" linkcheck_ignore = [ 'http://127.0.0.1', + 'http://104.154.71.112', 'https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#' ] intersphinx_mapping = { @@ -41,5 +42,5 @@ html_logo = '_static/anuket-logo.png' html_favicon = '_static/favicon.ico' -bibtex_bibfiles = ['refs.bib'] +bibtex_bibfiles = ['refs-rc2.bib'] bibtex_default_style = 'unsrt' diff --git a/doc/ref_cert/RC2/refs-rc2.bib b/doc/ref_cert/RC2/refs-rc2.bib new file mode 100644 index 0000000..3e87a78 --- /dev/null +++ b/doc/ref_cert/RC2/refs-rc2.bib @@ -0,0 +1,387 @@ +% Chapter 2 References + +@misc{rc2-kubernetes-e2e-tests, + title = {Kubernetes e2e Tests}, + url = {https://github.com/kubernetes/community/blob/master/contributors/devel/sig-testing/e2e-tests.md} +} + +@misc{rc2-kubernetes-conformance, + title = {Kubernetes Conformance Program}, + url = {https://github.com/cncf/k8s-conformance} +} + +@misc{rc2-kubernetes-sig-testing, + title = {Kubernetes Conformance Program}, + url = {https://github.com/kubernetes/community/blob/master/sig-testing/charter.md} +} + + +@misc{rc2-ra2, + title = {Cloud Infrastructure Reference Architecture managed by Kubernetes}, + howpublished = {GSMA PRD NG.139 v1.0}, + publisher = {GSM Association}, + year = 2023 +} + +@misc{rc2-sonobuoy-e2eplugin, + title = {Sonobuoy Kubernetes End-To-End Testing Plugin}, + url = {https://sonobuoy.io/docs/main/e2eplugin/} +} + +@misc{rc2-sonobuoy, + title = {Sonobuoy}, + url = {https://sonobuoy.io/} +} + +@misc{rc2-k8s-api-sig-api-machinery, + title = {Kubernetes API Machinery Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-api-machinery} +} + +@misc{rc2-k8s-api-sig-apps, + title = {Kubernetes Apps Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-apps} +} + +@misc{rc2-k8s-api-sig-auth, + title = {Kubernetes Auth Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-auth} +} + +@misc{rc2-k8s-api-sig-cluster-lifecycle, + title = {Kubernetes Cluster Lifecycle Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-cluster-lifecycle} +} + +@misc{rc2-k8s-api-sig-instrumentation, + title = {Kubernetes Instrumentation Special Interest Group }, + url = {https://github.com/kubernetes/community/tree/master/sig-instrumentation} +} + +@misc{rc2-sig-release-1.29-blocking, + title = {Kubernetes Release Special Interest Group Release 1.29 Jobs}, + url = {https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes/sig-release/release-branch-jobs/1.29.yaml} +} + +@misc{rc2-functest-k8s-jobs-1.29, + title = {Functest Jobs for Kubernetes 1.29 testing}, + url = {http://104.154.71.112:8080/job/functest-kubernetes-v1.29-daily} +} + +@misc{rc2-k8s-api-sig-network, + title = {Kubernetes Network Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-network} +} + +@misc{rc2-k8s-api-sig-node, + title = {Kubernetes Node Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-node} +} + +@misc{rc2-k8s-api-sig-scheduling, + title = {Kubernetes Scheduling Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-scheduling} +} + +@misc{rc2-k8s-api-sig-storage, + title = {Kubernetes Storage Special Interest Group}, + url = {https://github.com/kubernetes/community/tree/master/sig-storage} +} + +@misc{rc2-k8s-kind-sig-issue-2356, + title = {Add git to kindest/node #2356}, + url = {https://github.com/kubernetes-sigs/kind/issues/2356} +} + +@misc{rc2-openstack-rally, + title = {Rally}, + url = {https://github.com/openstack/rally} +} + +@misc{rc2-functest-kubernetes-benchmarking, + title = {Functest Kubernetes Benchmarking}, + url = {https://git.opnfv.org/functest-kubernetes/tree/docker/benchmarking/testcases.yaml?h=stable%2Fv1.29} +} + +@misc{rc2-xrally-kubernetes-full, + title = {xrally_kubernetes_full}, + url = {https://artifacts.opnfv.org/functest-kubernetes/671YK0WH9PRK/functest-kubernetes-opnfv-functest-kubernetes-benchmarking-v1.29-xrally_kubernetes_full-run-9/xrally_kubernetes_full/xrally_kubernetes_full.html} +} + +@misc{rc2-xrally-kubernetes, + title = {xrally-kubernetes}, + url = {https://github.com/xrally/xrally-kubernetes} +} + +@misc{rc2-k8s-perf-tests, + title = {Kubernetes perf-tests}, + url = {https://github.com/kubernetes/perf-tests} +} + +@misc{rc2-k8s-netperf, + title = {Kubernetes netperf}, + url = {https://github.com/kubernetes/perf-tests/tree/master/network/benchmarks/netperf} +} + +@misc{rc2-iperf, + title = {iperf}, + url = {https://github.com/esnet/iperf} +} + +@misc{rc2-netperf, + title = {netperf}, + url = {https://github.com/HewlettPackard/netperf/} +} + +@misc{rc2-functest-kubernetes-security, + title = {Functest Kubernetes Security}, + url = {https://git.opnfv.org/functest-kubernetes/tree/docker/security/testcases.yaml?h=stable%2Fv1.29} +} + +@misc{rc2-kube-hunter, + title = {kube-hunter}, + url = {https://github.com/aquasecurity/kube-hunter} +} + +@misc{rc2-kube-bench, + title = {kube-bench}, + url = {https://github.com/aquasecurity/kube-bench} +} + +@misc{rc2-cis-kubernetes-benchmark, + title = {kube-bench}, + url = {https://github.com/aquasecurity/kube-bench} +} + +@misc{rc2-kube-hunter-vulnerability-categories, + title = {kube-bench vulerability categories}, + url = {https://github.com/aquasecurity/kube-hunter/blob/v0.6.8/kube_hunter/core/events/types.py} +} + +@misc{rc2-clearwater-ims, + title = {Clearwater IMS}, + url = {https://github.com/Metaswitch/clearwater-docker} +} + +@misc{rc2-clearwater-live-test, + title = {clearwater-live-test}, + url = {https://github.com/Metaswitch/clearwater-live-test} +} + +% Chapter 3 References + +@misc{rc2-ansible-xtesting-ci, + title = {Xtesting CI}, + url = {https://galaxy.ansible.com/collivier/xtesting} +} + +@misc{rc2-ansible-role-xtesting-ci, + title = {Deploy your own Xtesting CI/CD toolchains}, + url = {https://github.com/collivier/ansible-role-xtesting} +} + +@misc{rc2-docker-proxy, + title = {Docker documentation on Proxy settings}, + url = {https://docs.docker.com/config/daemon/systemd/#httphttps-proxy} +} + +% Chapter 4 References + +@misc{rc2-r8s-doc-hostpath-volume, + title = {hostPath volumes}, + url = {https://kubernetes.io/docs/concepts/storage/volumes/#hostpath} +} + +@misc{rc2-k8s-nfd, + title = {Kubernetes Node Feature Discovery}, + url = {https://kubernetes-sigs.github.io/node-feature-discovery/stable/get-started/index.html} +} + +@misc{rc2-cnti-testsuite-helm_chart_published, + title = {CNF Testsuite, Rationale, Test if the Helm chart is published: helm_chart_published}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-helm-chart-is-published-helm_chart_published} +} + +@misc{rc2-cnti-testsuite-helm_chart_valid, + title = {CNF Testsuite, Rationale, Test if the Helm chart is valid: helm_chart_valid}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-helm-chart-is-valid-helm_chart_valid} +} + +@misc{rc2-cnti-testsuite-rolling_update, + title = {CNF Testsuite, Rationale, To test if the CNF can perform a rolling update: rolling_update}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-if-the-cnf-can-perform-a-rolling-update-rolling_update} +} + +@misc{rc2-cnti-testsuite-rolling_downgrade, + title = {CNF Testsuite, Rationale, To check if a CNF version can be downgraded through a rolling_downgrade: rolling_downgrade}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-a-cnf-version-can-be-downgraded-through-a-rolling_downgrade-rolling_downgrade} +} + +@misc{rc2-cnti-testsuite-cni_compatibility, + title = {CNF Testsuite, Rationale, To check if the CNF is compatible with different CNIs: cni_compatibility}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-the-cnf-is-compatible-with-different-cnis-cni_compatibility} +} + +@misc{rc2-cnti-testsuite-alpha_k8s_apis, + title = {CNF Testsuite, Rationale, To check if a CNF uses Kubernetes alpha APIs 'alpha_k8s_apis': alpha_k8s_apis}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#poc-to-check-if-a-cnf-uses-kubernetes-alpha-apis-alpha_k8s_apis-alpha_k8s_apis} +} + + +@misc{rc2-cnti-testsuite-pod_delete, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when pod delete occurs: pod_delete}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-pod-delete-occurs-pod_delete} +} + +@misc{rc2-cnti-testsuite-node_drain, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when node drain occurs: node_drain}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-node-drain-occurs-node_drain} +} + +@misc{rc2-cnti-testsuite-pod_network_latency, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when network latency occurs: pod_network_latency}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-network-latency-occurs-pod_network_latency} +} + + +@misc{rc2-cnti-testsuite-disk_fill, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when disk fill occurs: disk_fill}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-disk-fill-occurs} +} + +@misc{rc2-cnti-testsuite-pod_memory_hog, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when pod memory hog occurs: pod_memory_hog}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-pod-memory-hog-occurs-pod_memory_hog} +} + +@misc{rc2-cnti-testsuite-pod_io_stress, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when pod io stress occurs: pod_io_stress}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-pod-io-stress-occurs-pod_io_stress} +} + +@misc{rc2-cnti-testsuite-pod_network_corruption, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when pod network corruption occurs: pod_network_corruption}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-pod-network-corruption-occurs-pod_network_corruption} +} + +@misc{rc2-cnti-testsuite-pod_network_duplication, + title = {CNF Testsuite, Rationale, Test if the CNF crashes when pod network duplication occurs: pod_network_duplication}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#test-if-the-cnf-crashes-when-pod-network-duplication-occurs-pod_network_duplication} +} + +@misc{rc2-cnti-testsuite-no_local_volume_configuration, + title = {CNF Testsuite, Rationale, To test if the CNF uses local storage: no_local_volume_configuration}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-if-the-cnf-uses-local-storage-no_local_volume_configuration} +} + +@misc{rc2-cnti-testsuite-liveness, + title = {CNF Testsuite, Rationale, To test if there is a liveness entry in the Helm chart: liveness}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-if-there-is-a-liveness-entry-in-the-helm-chart-liveness} +} + +@misc{rc2-cnti-testsuite-readiness, + title = {CNF Testsuite, Rationale, To test if there is a readiness entry in the Helm chart: readiness}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-if-there-is-a-readiness-entry-in-the-helm-chart-readiness} +} + +@misc{rc2-cnti-testsuite-service_account_mapping, + title = {CNF Testsuite, Rationale, To check if there is automatic mapping of service accounts: service_account_mapping}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-there-is-automatic-mapping-of-service-accounts-service_account_mapping} +} + +@misc{rc2-cnti-testsuite-host_network, + title = {CNF Testsuite, Rationale, To check if there is a host network attached to a pod: host_network}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-there-is-a-host-network-attached-to-a-pod-host_network} +} + +@misc{rc2-cnti-testsuite-host_pid_ipc_privileges, + title = {CNF Testsuite, Rationale, To check if containers are running with hostPID or hostIPC privileges: host_pid_ipc_privileges}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-containers-are-running-with-hostpid-or-hostipc-privileges-host_pid_ipc_privileges} +} + +@misc{rc2-cnti-testsuite-resource_policies, + title = {CNF Testsuite, Rationale, To check if containers have resource limits defined: resource_policies}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-containers-have-resource-limits-defined-resource_policies} +} + +@misc{rc2-cnti-testsuite-immutable_file_systems, + title = {CNF Testsuite, Rationale, To check if containers have immutable file systems: immutable_file_systems}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-containers-have-immutable-file-systems-immutable_file_systems} +} + +@misc{rc2-cnti-testsuite-hardcoded_ip_addresses_in_k8s_runtime_configuration, + title = {CNF Testsuite, Rationale, To test if there are any (non-declarative) hardcoded IP addresses or subnet masks in the K8s runtime configuration: hardcoded_ip_addresses_in_k8s_runtime_configuration}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-if-there-are-any-non-declarative-hardcoded-ip-addresses-or-subnet-masks-in-the-k8s-runtime-configuration-hardcoded_ip_addresses_in_k8s_runtime_configuration} +} + +@misc{rc2-cnti-testsuite-increase_decrease_capacity, + title = {CNF Testsuite, Rationale, To test the increasing and decreasing of capacity: increase_decrease_capacity}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-the-increasing-and-decreasing-of-capacity-increase_decrease_capacity} +} + +@misc{rc2-cnti-testsuite-reasonable_image_size, + title = {CNF Testsuite, Rationale, To check if the CNF has a reasonable image size: reasonable_image_size}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-the-cnf-has-a-reasonable-image-size-reasonable_image_size} +} + +@misc{rc2-cnti-testsuite-reasonable_startup_time, + title = {CNF Testsuite, Rationale, To check if the CNF have a reasonable startup time: reasonable_startup_time}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-the-cnf-have-a-reasonable-startup-time-reasonable_startup_time} +} + +@misc{rc2-cnti-testsuite-privileged_containers, + title = {CNF Testsuite, Rationale, To check if there are any privileged containers: privileged_containers}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-there-are-any-privileged-containers-privileged_containers} +} + +@misc{rc2-cnti-testsuite-non_root_user, + title = {CNF Testsuite, Rationale, To check if any containers are running as a root user (checks the user outside the container that is running dockerd): non_root_user}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-any-containers-are-running-as-a-root-user-checks-the-user-outside-the-container-that-is-running-dockerd-non_root_user} +} + +@misc{rc2-cnti-testsuite-privilege_escalation, + title = {CNF Testsuite, Rationale, To check if any containers allow for privilege escalation: privilege_escalation}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-any-containers-allow-for-privilege-escalation-privilege_escalation} +} + +@misc{rc2-cnti-testsuite-non_root_containers, + title = {CNF Testsuite, Rationale, To check if containers are running with non-root user with non-root membership: non_root_containers}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-check-if-containers-are-running-with-non-root-user-with-non-root-membership-non_root_containers} +} + +@misc{rc2-cnti-testsuite-hostport_not_used, + title = {CNF Testsuite, Rationale, To test if there are host ports used in the service configuration: hostport_not_used}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/RATIONALE.md#to-test-if-there-are-host-ports-used-in-the-service-configuration-hostport_not_used} +} + +@misc{rc2-k8s-service, + title = {Kubernetes documentation: Service}, + url = {https://kubernetes.io/docs/concepts/services-networking/service/} +} + +@misc{rc2-k8s-configmap-immutable, + title = {Kubernetes documentation: Immutable ConfigMaps}, + url = {https://kubernetes.io/docs/concepts/configuration/configmap/#configmap-immutable} +} + +@misc{rc2-k8s-hpa, + title = {Kubernetes documentation: Horizontal Pod Autoscaling}, + url = {https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/} +} + +@misc{rc2-k8s-recommended-labels, + title = {Kubernetes documentation: Recommended Labels}, + url = {https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/} +} + +% Chapter 5 References + +@misc{rc2-cnti-testsuite-list-of-tests, + title = {CNTI, CNF Testsuite, List of tests}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/docs/LIST_OF_TESTS.md} +} + +@misc{rc2-cnti-testsuite-install, + title = {CNTI, CNF Testsuite, Install guide}, + url = {https://github.com/cnti-testcatalog/testsuite/blob/main/INSTALL.md} +} From ed625213c0b121aa8759dd68290892f48fe4e2a0 Mon Sep 17 00:00:00 2001 From: Gergely Csatari Date: Mon, 3 Jun 2024 13:31:10 +0300 Subject: [PATCH 9/9] Adding GSMA build Signed-off-by: Gergely Csatari --- doc/ref_cert/RC2/template.docx | Bin 0 -> 71626 bytes doc/ref_cert/RC2/togsma.py | 149 +++++++++++++++++++++++++++++++++ doc/ref_cert/RC2/tox.ini | 19 +++++ 3 files changed, 168 insertions(+) create mode 100644 doc/ref_cert/RC2/template.docx create mode 100644 doc/ref_cert/RC2/togsma.py diff --git a/doc/ref_cert/RC2/template.docx b/doc/ref_cert/RC2/template.docx new file mode 100644 index 0000000000000000000000000000000000000000..0c87cdb5bfe07d3f4caaa81f897829ecda02d7f8 GIT binary patch literal 71626 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 Q_@fIfFrJ_TQxDw#0yZ~LkpKVy literal 0 HcmV?d00001 diff --git a/doc/ref_cert/RC2/togsma.py b/doc/ref_cert/RC2/togsma.py new file mode 100644 index 0000000..fea457a --- /dev/null +++ b/doc/ref_cert/RC2/togsma.py @@ -0,0 +1,149 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring + +import os +import shutil +import sys +import re + +from pybtex.database import parse_file +from sphinx.ext import intersphinx + + +class MockConfig: + # pylint: disable=too-few-public-methods + intersphinx_timeout: int = None + tls_verify = False + user_agent = None + + +class MockApp: + # pylint: disable=too-few-public-methods,no-self-use + # pylint: disable=missing-function-docstring + srcdir = '' + config = MockConfig() + + def warn(self, msg: str) -> None: + print(msg, file=sys.stderr) + +# Assuming that the last header of Chapter01 is the references it is not needed to have it here. +HEADER_REFERENCES = """ + +.. list-table:: References + :widths: auto + + * - Ref + - Doc Number + - Title +""" + +HEADER_BIBLIOGRAPHY = """Bibliography +------------ + +.. list-table:: Bibliography + :widths: auto + + * - Ref + - Document Title + - Source +""" + + +invdata = intersphinx.fetch_inventory(MockApp(), '', "build/objects.inv") +bib_data = parse_file('refs-rc2.bib') + +shutil.rmtree('gsma', ignore_errors=True) +os.mkdir('gsma') +shutil.copytree('figures', 'gsma/figures') +shutil.copy('conf.py', 'gsma/conf.py') +shutil.copy('refs-rc2.bib', 'gsma/refs-rc2.bib') + +with open("gsma/references.rst", "w", encoding='utf-8') as references, open( + "gsma/bibliography.rst", "w", encoding='utf-8') as bibliography: + references.write(HEADER_REFERENCES) + bibliography.write(HEADER_BIBLIOGRAPHY) + i = 1 + + for key in bib_data.entries: + if 'howpublished' in bib_data.entries[key].fields: + references.write(f" * - [{i}]\n") + references.write( + f" - {bib_data.entries[key].fields['howpublished']}\n") + references.write( + f" - {bib_data.entries[key].fields['title']}\n") + else: + bibliography.write(f" * - [{i}]\n") + bibliography.write( + f" - {bib_data.entries[key].fields['title']}\n") + bibliography.write( + f" - {bib_data.entries[key].fields['url']}\n") + i = i + 1 + +filenames = ['chapters/chapter01.rst', + 'gsma/references.rst', + 'gsma/bibliography.rst', + 'chapters/chapter02.rst', + 'chapters/chapter03.rst', + 'chapters/chapter04.rst', + 'chapters/chapter05.rst', + 'chapters/chapter06.rst'] + +with open('gsma/index.rst', 'w', encoding='utf-8') as outfile: + for fname in filenames: + with open(fname, encoding='utf-8') as infile: + for line in infile: + if (".. bibliography::" not in line.strip("\n") and + ":cited:" not in line.strip("\n")): + outfile.write(line) + outfile.write('\n') + +with open('gsma/index.rst', 'r', encoding='utf-8') as infile: + filedata = infile.read() + i = 1 + for key in bib_data.entries: + if 'howpublished' in bib_data.entries[key].fields: + filedata = filedata.replace( + f":cite:p:`{bib_data.entries[key].key}`", + f"`[{i}] <#references>`_") + filedata = filedata.replace( + f":cite:t:`{bib_data.entries[key].key}`", + f"{bib_data.entries[key].fields['title']} `[{i}] <#references>`_") + + else: + print(f"ref: changing {bib_data.entries[key].key} to {bib_data.entries[key].fields['url']}") + filedata = filedata.replace( + f":cite:p:`{bib_data.entries[key].key}`", + f"`[{i}] <{bib_data.entries[key].fields['url']}>`__") + filedata = filedata.replace( + f":cite:t:`{bib_data.entries[key].key}`", + f"{bib_data.entries[key].fields['title']} `[{i}] <{bib_data.entries[key].fields['url']}>`__") + + i = i + 1 + + for key in sorted(invdata or {}): + if 'std:label' in key: + for entry, einfo in sorted(invdata[key].items()): + if "chapters/" in entry: + filedata = filedata.replace( + f":ref:`{entry}`", + f"`{einfo[3]}`_") + + filedata = filedata.replace("../figures", "figures") + + ### Adjusting title underlines. Note this function is very specific to RA2 Ch6 where there are links in the titles + # and all of these headers are underlined with --- + filedatalines = filedata.splitlines(True) + # Going through on all lines + # line is only used to get the real index to the index variable + for index, line in enumerate(filedatalines): + # To work directly on the data it needs to be addressed in the original filedatalines array. + # Looking for title underlines --- + if "---" in str(filedatalines[index]): + if len(filedatalines[index]) < len(filedatalines[index - 1]): + filedatalines[index] = filedatalines[index].rjust(len(filedatalines[index - 1]), '-') + # write is back to one string + filedata = "".join(filedatalines) + +with open('gsma/index.rst', 'w', encoding='utf-8') as outfile: + outfile.write(filedata) + os.remove("gsma/references.rst") + os.remove("gsma/bibliography.rst") \ No newline at end of file diff --git a/doc/ref_cert/RC2/tox.ini b/doc/ref_cert/RC2/tox.ini index 2e4260e..1e6a101 100644 --- a/doc/ref_cert/RC2/tox.ini +++ b/doc/ref_cert/RC2/tox.ini @@ -14,3 +14,22 @@ commands = sphinx-build --keep-going -W -b latex . build sphinx-build --keep-going -W -b linkcheck . build +[testenv:gsma] +basepython = python3.10 +deps = + -r{toxinidir}/test-requirements.txt +install_command = pip install {opts} {packages} +allowlist_externals = + pandoc + bash + rm +commands = + rm -rf build + rm -rf gsma + doc8 . --ignore-path .tox --ignore-path build --ignore-path gsma --ignore D001 + sphinx-build --keep-going -W -b html . build + sphinx-build --keep-going -W -b linkcheck . build + python togsma.py + bash -c '(cd gsma && sphinx-build --keep-going -b html . build)' + bash -c '(cd gsma && sphinx-build --keep-going -b latex . build)' + pandoc -s --reference-doc=template.docx -o gsma/rc2.docx -t docx gsma/index.rst