diff --git a/Makefile b/Makefile index 5dbe1af51..b9d8f1e42 100644 --- a/Makefile +++ b/Makefile @@ -31,12 +31,7 @@ prod: prod-build verify: fmt vet -godep-restore: check-gopath - godep restore - rm -rf vendor Godeps - -godep-save: check-gopath - godep save ./... +docs: _docs clean: rm -rf _docker_workspace @@ -118,3 +113,23 @@ reset-dev-patch: # Build devloper image dev: dev-patch prod-quick reset-dev-patch + +# Docs +# +doc-preview: + rm -rf docs/_build + DOCKER_RUN_ARGS="-p 127.0.0.1:8000:8000" \ + ./build-tools/docker-docs.sh make -C docs preview + +_docs: always-build + ./build-tools/docker-docs.sh ./build-tools/make-docs.sh + +docker-test: + rm -rf docs/_build + ./build-tools/docker-docs.sh ./build-tools/make-docs.sh + +# one-time html build using a docker container +.PHONY: docker-html +docker-html: + rm -rf docs/_build + ./build-tools/docker-docs.sh make -C docs/ html diff --git a/build-tools/Dockerfile.debian.runtime b/build-tools/Dockerfile.debian.runtime index 71dd233fe..ea0b59e50 100755 --- a/build-tools/Dockerfile.debian.runtime +++ b/build-tools/Dockerfile.debian.runtime @@ -21,7 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get remove -y libidn11 COPY bigip-virtual-server_v*.json $APPPATH/vendor/src/f5/schemas/ -COPY as3-schema-3.20.0-3-cis.json $APPPATH/vendor/src/f5/schemas/ +COPY as3-schema-3.21.0-4-cis.json $APPPATH/vendor/src/f5/schemas/ COPY k8s-bigip-ctlr $APPPATH/bin COPY VERSION_BUILD.json $APPPATH/vendor/src/f5/ diff --git a/build-tools/Dockerfile.debug.runtime b/build-tools/Dockerfile.debug.runtime index 1ef7a3844..6e7e74105 100644 --- a/build-tools/Dockerfile.debug.runtime +++ b/build-tools/Dockerfile.debug.runtime @@ -26,7 +26,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get remove -y libidn11 COPY bigip-virtual-server_v*.json $APPPATH/vendor/src/f5/schemas/ -COPY as3-schema-3.20.0-3-cis.json $APPPATH/vendor/src/f5/schemas/ +COPY as3-schema-3.21.0-4-cis.json $APPPATH/vendor/src/f5/schemas/ COPY k8s-bigip-ctlr $APPPATH/bin COPY VERSION_BUILD.json $APPPATH/vendor/src/f5/ COPY --from=builder /go/bin/dlv /app/bin diff --git a/build-tools/Dockerfile.rhel7.runtime b/build-tools/Dockerfile.rhel7.runtime index 4a04f643e..52d72df0f 100644 --- a/build-tools/Dockerfile.rhel7.runtime +++ b/build-tools/Dockerfile.rhel7.runtime @@ -39,7 +39,7 @@ RUN microdnf --enablerepo=rhel-7-server-rpms --enablerepo=rhel-7-server-optional microdnf clean all COPY bigip-virtual-server_v*.json $APPPATH/vendor/src/f5/schemas/ -COPY as3-schema-3.20.0-3-cis.json $APPPATH/vendor/src/f5/schemas/ +COPY as3-schema-3.21.0-4-cis.json $APPPATH/vendor/src/f5/schemas/ COPY k8s-bigip-ctlr $APPPATH/bin/k8s-bigip-ctlr.real COPY VERSION_BUILD.json $APPPATH/vendor/src/f5/ diff --git a/build-tools/build-release-images.sh b/build-tools/build-release-images.sh index 0bbaf1f26..5b9e1fd8e 100755 --- a/build-tools/build-release-images.sh +++ b/build-tools/build-release-images.sh @@ -36,7 +36,7 @@ docker rm -f cp-temp cp requirements.txt $WKDIR/ cp schemas/bigip-virtual-server_v*.json $WKDIR/ -cp schemas/as3-schema-3.20.0-3-cis.json $WKDIR/ +cp schemas/as3-schema-3.21.0-4-cis.json $WKDIR/ cp LICENSE $WKDIR/ cp $CURDIR/help.md $WKDIR/help.md echo "{\"version\": \"${VERSION_INFO}\", \"build\": \"${BUILD_INFO}\"}" \ diff --git a/cmd/k8s-bigip-ctlr/main.go b/cmd/k8s-bigip-ctlr/main.go index 61fa51d1c..b7bc3ceef 100644 --- a/cmd/k8s-bigip-ctlr/main.go +++ b/cmd/k8s-bigip-ctlr/main.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -177,12 +177,12 @@ var ( ) func _init() { - flags = pflag.NewFlagSet("main", pflag.ContinueOnError) - globalFlags = pflag.NewFlagSet("Global", pflag.ContinueOnError) - bigIPFlags = pflag.NewFlagSet("BigIP", pflag.ContinueOnError) - kubeFlags = pflag.NewFlagSet("Kubernetes", pflag.ContinueOnError) - vxlanFlags = pflag.NewFlagSet("VXLAN", pflag.ContinueOnError) - osRouteFlags = pflag.NewFlagSet("OpenShift Routes", pflag.ContinueOnError) + flags = pflag.NewFlagSet("main", pflag.PanicOnError) + globalFlags = pflag.NewFlagSet("Global", pflag.PanicOnError) + bigIPFlags = pflag.NewFlagSet("BigIP", pflag.PanicOnError) + kubeFlags = pflag.NewFlagSet("Kubernetes", pflag.PanicOnError) + vxlanFlags = pflag.NewFlagSet("VXLAN", pflag.PanicOnError) + osRouteFlags = pflag.NewFlagSet("OpenShift Routes", pflag.PanicOnError) // Flag wrapping var err error @@ -692,6 +692,11 @@ func initCustomResourceManager( } func main() { + defer func() { + if r := recover(); r != nil { + flags.Usage() + } + }() err := flags.Parse(os.Args) if nil != err { os.Exit(1) diff --git a/cmd/k8s-bigip-ctlr/main_test.go b/cmd/k8s-bigip-ctlr/main_test.go index 145031472..c3e31aefb 100644 --- a/cmd/k8s-bigip-ctlr/main_test.go +++ b/cmd/k8s-bigip-ctlr/main_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -595,16 +595,18 @@ var _ = Describe("Main Tests", func() { } flags.SetOutput(MockOut{}) + defer func() { + Expect(called).To(BeTrue()) + }() defer flags.SetOutput(os.Stderr) - + defer func() { + if r := recover(); r != nil { + flags.Usage() + } + }() err := flags.Parse(os.Args) Expect(err).ToNot(BeNil()) - // This implementation changed in spf13 v1.0.3. - // Usage() is not called as ContinueOnError is set for unit tests. - // So we adopted to the new behaviour introduced. - // Refer --> FlagSet.failf() in flag.go - Expect(called).To(BeFalse()) - Expect(len(*openshiftSDNName)).To(Equal(0)) + }) It("sets up watches for all namespaces", func() { diff --git a/cmd/k8s-bigip-ctlr/pythonDriver.go b/cmd/k8s-bigip-ctlr/pythonDriver.go index 4e7474460..03ce232a1 100644 --- a/cmd/k8s-bigip-ctlr/pythonDriver.go +++ b/cmd/k8s-bigip-ctlr/pythonDriver.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2019, F5 Networks, Inc. + * Copyright (c) 2019-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/docs/README.rst b/docs/README.rst index a560f4251..c8877b45b 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -125,7 +125,7 @@ General .. note:: - - The :code:`python-basedir` setting lets you specify the path to an alternate python agent that can bridge between the |kctlr| and `F5 CCCL `_. + - The :code:`python-basedir` setting lets you specify the path to an alternate python agent that can bridge between the |kctlr| and F5-CCCL. - The time it takes for the |kctlr| to reapply the system configurations to the BIG-IP device is normally low (a few ms) and won't cause service disruption. @@ -359,7 +359,7 @@ See the `Integration Overview`_ for more information about F5 resources. | f5type | Tells ``k8s-bigip-ctlr`` about resources it | | | | should watch | | +---------------+---------------------------------------------------+-----------------------------------------------+ -| schema | Verifies the ``data`` blob | See the `F5 schema versions`_ table | +| schema | Verifies the ``data`` blob | See the F5 schema versions table | +---------------+---------------------------------------------------+-----------------------------------------------+ | data | Defines the F5 resource | | +---------------+---------------------------------------------------+-----------------------------------------------+ @@ -379,7 +379,7 @@ The `F5 schema`_ allows the |kctlr| to communicate with BIG-IP systems. While all versions of the BIG-IP Controller and F5 schema are backwards-compatible, using an older schema may limit Controller functionality. Be sure to use the schema version that corresponds with your Controller version to ensure access to the full feature set. - See the `F5 schema versions`_ table for schema and Controller version compatibility. + See the F5 schema versions table for schema and Controller version compatibility. .. _frontend: @@ -434,11 +434,11 @@ sslProfile [#ssl]_ JSON object Optional BIG-IP S .. note:: - If you include ``virtualAddress`` in your Resource definition, you can specify the ``bindAddr`` and ``port`` you want the virtual server to use. Omit the ``virtualAddress`` section if you want to create `pools without virtual servers`_. + If you include ``virtualAddress`` in your Resource definition, you can specify the ``bindAddr`` and ``port`` you want the virtual server to use. Omit the ``virtualAddress`` section if you want to create pools without virtual servers. If you're creating pools without virtual servers, **you should already have a BIG-IP virtual server** that handles client connections configured with an iRule or local traffic policy that can forward requests to the correct pool for the Service. - You can also `assign IP addresses to BIG-IP virtual servers using IPAM`_. + You can also assign IP addresses to BIG-IP virtual servers using IPAM. .. _iapp f5 resource: @@ -800,29 +800,29 @@ Example Configuration Files - :fonticon:`fa fa-download` :download:`example-vs-resource.json ` - :fonticon:`fa fa-download` :download:`example-vs-resource-iapp.json ` - :fonticon:`fa fa-download` :download:`example-advanced-vs-resource-iapp.json ` -- :fonticon:`fa fa-download` :download:`single-service-ingress.yaml ` -- :fonticon:`fa fa-download` :download:`single-service-tls-ingress.yaml ` -- :fonticon:`fa fa-download` :download:`simple-ingress-fanout.yaml ` -- :fonticon:`fa fa-download` :download:`name-based-ingress.yaml ` -- :fonticon:`fa fa-download` :download:`ingress-with-health-monitors.yaml ` +- :fonticon:`fa fa-download` :download:`single-service-ingress.yaml ` +- :fonticon:`fa fa-download` :download:`single-service-tls-ingress.yaml ` +- :fonticon:`fa fa-download` :download:`simple-ingress-fanout.yaml ` +- :fonticon:`fa fa-download` :download:`name-based-ingress.yaml ` +- :fonticon:`fa fa-download` :download:`ingress-with-health-monitors.yaml ` - :fonticon:`fa fa-download` :download:`sample-rbac.yaml ` -- :fonticon:`fa fa-download` :download:`sample-app-root-annotation.yaml ` -- :fonticon:`fa fa-download` :download:`sample-url-rewrite-annotation.yaml ` +- :fonticon:`fa fa-download` :download:`sample-app-root-annotation.yaml ` +- :fonticon:`fa fa-download` :download:`sample-url-rewrite-annotation.yaml ` OpenShift ````````` -- :fonticon:`fa fa-download` :download:`sample-unsecured-route.yaml ` -- :fonticon:`fa fa-download` :download:`sample-edge-route.yaml ` -- :fonticon:`fa fa-download` :download:`sample-passthrough-route.yaml ` -- :fonticon:`fa fa-download` :download:`sample-reencrypt-route.yaml ` +- :fonticon:`fa fa-download` :download:`sample-unsecured-route.yaml ` +- :fonticon:`fa fa-download` :download:`sample-edge-route.yaml ` +- :fonticon:`fa fa-download` :download:`sample-passthrough-route.yaml ` +- :fonticon:`fa fa-download` :download:`sample-reencrypt-route.yaml ` .. rubric:: **Footnotes** .. [#objectpartition] The |kctlr| creates and manages objects in the BIG-IP partition defined in the `F5 resource`_ ConfigMap. **It cannot manage objects in the** ``/Common`` **partition**. .. [#nodeportmode] The |kctlr| forwards traffic to the NodePort assigned to the Service by Kubernetes. See the `Kubernetes Service`_ documentation for more information. -.. [#lb] The |kctlr| supports BIG-IP load balancing algorithms that do not require additional configuration parameters. You can view the full list of supported algorithms in the `f5-cccl schema `_. See the `BIG-IP Local Traffic Management Basics user guide `_ for information about each load balancing mode. +.. [#lb] The |kctlr| supports BIG-IP load balancing algorithms that do not require additional configuration parameters. You can view the full list of supported algorithms in the f5-cccl schema. See the `BIG-IP Local Traffic Management Basics user guide `_ for information about each load balancing mode. .. [#ba] The Controller supports BIG-IP `route domain`_ specific addresses. .. [#ssl] If you want to configure multiple SSL profiles, use ``f5ProfileNames`` instead of ``f5ProfileName``. The two parameters are mutually exclusive. .. [#hm1] Required if defining the ``virtual-server.f5.com/health`` Ingress/Route annotation. diff --git a/docs/RELEASE-NOTES.rst b/docs/RELEASE-NOTES.rst index ffacbba54..91ceed1a3 100644 --- a/docs/RELEASE-NOTES.rst +++ b/docs/RELEASE-NOTES.rst @@ -1,11 +1,40 @@ Release Notes for Container Ingress Services for Kubernetes & OpenShift ======================================================================= -Next Release ------------- +2.1.1 +------------- +Added Functionality +````````````````````` +* CIS is now compatible with: + - OpenShift 4.5. + - AS3 3.21. +* Custom Resource Definition (CRD) – Preview version available with `virtual-server` and `TLSProfile` custom resources. + - `CRD Doc and Examples `_. +* Custom Resource Definition (CRD) – Added Support for k8s Secrets with TLSProfile Custom Resource. +* Custom Resource Definition (CRD) – Improved the strategy of processing `virtual-server` and `TLSProfile` custom resources. +* Custom Resource Definition (CRD) – Added support for installation using Helm and Operator. +* Custom Resource Definition (CRD) – Streamlined logs to provide insightful information in INFO and remove unwanted information in DEBUG mode. Bug Fixes ````````` +* :issues:`1467` AS3 ERROR declaration.schemaVersion must be one of the following with Controller version 2.1.0. +* :issues:`1433` Template is not valid. When using CIS 2.1 with AS3 version: 3.21.0. +* :issues:`1440` Optional health check parameters don't appear to be optional. +* Fixed issues with processing multiple services with same annotations in AS3 ConfigMap mode. + - When there are multiple services with same annotations, CIS updates the oldest service endpoints in BIG-IP. +* Fixed issues with continuous AS3 declarations in CRD mode. +* Fixed issues with re-encrypt termination on multiple domains in CRD mode. +* Fixed issues with crashing of CIS in CRD mode. + - When user removes f5cr label from `VirtualServer` or `TLSProfile` custom resources. + - When user deletes `TLSProfile` custom resource. This behaviour is intermittent. +* Fixed issues with processing of unwanted endpoint and service changes in CRD mode. + +Limitations +``````````` +* During restarts, CIS fails to read `TLSProfile` custom resource. This behaviour is intermittent. +* CIS does not update the endpoint changes on BIG-IP in CRD mode. This behaviour is intermittent. +* CIS does not validate secrets and BIG-IP profiles provided in `TLSProfile` custom resource. +* CIS supports only port 80 and 443 for BIG-IP Virtual servers in CRD mode. 2.1 ------------- @@ -13,9 +42,9 @@ Added Functionality ``````````````````` * CIS will not create `_AS3` partition anymore. - CIS uses single partition(i.e. `--bigip-partition`) to configure both LTM and NET configuration. - - Additional AS3 managed partition _AS3 will be removed if exists. + - Removes Additional AS3 managed partition _AS3, if exists. * Enhanced performance for lower BIG-IP CPU Utilization with optimized CCCL calls. -* AS3 versions >= 3.18 required for CIS 2.x releases. +* CIS 2.x releases requires AS3 versions >= 3.18. * CIS is now compatible with: - OpenShift 4.4.5. - AS3 3.20. @@ -58,7 +87,7 @@ Vulnerability Fixes Archived CF and Mesos Github repos `````````````````````````````````` -* These GitHub repository has been archived and is read-only. This projects are no longer actively maintained +* This projects are no longer actively maintained - `cf-bigip-ctlr `_ - `marathon-bigip-ctlr `_ @@ -68,7 +97,7 @@ Guidelines for upgrading to CIS 2.1 - User should clean up LTM resources in BIG-IP partition created by CCCL before migrating to CIS 2.1. Steps to clean up LTM resources in BIG-IP partition using AS3 * Use below POST call along with this `AS3 declaration `_. - - https:///mgmt/shared/appsvcs/declare?async=true + - mgmt/shared/appsvcs/declare * Note: Please modify in above POST call and name in `AS3 declaration `_ 2.0 @@ -81,7 +110,7 @@ Added Functionality * Added new optional deployment arguments: - `--custom-resource-mode` (default `false`) when set `true` processes custom resources only. - `defined-as3-declaration` for processing user defined AS3 Config Map in CIS watched namespaces. -* AS3 versions >= 3.18 is required for 2.x releases. +* CIS Requires AS3 versions >= 3.18 for 2.x releases. * CIS is now compatible with: - OpenShift 4.3. - BIG-IP 15.1. @@ -117,11 +146,11 @@ Vulnerability Fixes Limitations ``````````` -* CIS with cccl as agent, OpenShift A/B route cannot be updated in BIGIP >=v14.1.x due to data group changes. +* CIS in cccl mode, cannot update OpenShift A/B route in BIGIP >=v14.1.x due to data group changes. Next Upgrade Notes `````````````````` -* From CIS 2.1, additional AS3 managed partition "_AS3" will be removed. +* CIS removes additional AS3 managed partition "_AS3" from release 2.1 1.14.0 ------------ @@ -429,7 +458,7 @@ Bug Fixes Limitations ``````````` -* Cannot apply app-root and url-rewrite annotations to the same resource; see: :issues:`675` +* Cannot apply app-root and url-rewrite annotations to the same resource; see: :issues:675 * If an older controller created resources, upgrading to the new version could result in a python exception when adding metadata to virtuals: :issues:`683` * If running the controller in cluster mode without a vxlan name, pool members are not created: :issues:`686` @@ -439,21 +468,21 @@ v1.4.2 Bug Fixes ````````` -* :issues:`549` - Using IP annotation on ConfigMaps would result in the virtual server getting a port of 0. -* :issues:`551` - Memory leak in python subprocess -* :cccl-issue:`211` - Memory leak in f5-cccl submodule -* :issues:`555` - Controller high CPU usage when inactive -* :issues:`510` - Change behavior of controller on startup when encountering errors -* :issues:`567` - Clean up all objects (including iRules and datagroups) when deleting Routes. +* :issues:549 - Using IP annotation on ConfigMaps would result in the virtual server getting a port of 0. +* :issues:551 - Memory leak in python subprocess +* :cccl-issue:211 - Memory leak in f5-cccl submodule +* :issues:555 - Controller high CPU usage when inactive +* :issues:510 - Change behavior of controller on startup when encountering errors +* :issues:567 - Clean up all objects (including iRules and datagroups) when deleting Routes. v1.4.1 ------ Bug Fixes ````````` -* :issues:`517` - Controller deletes SSL profiles off of Ingress virtual servers if watching multiple namespaces. -* :issues:`471` - When updating routes, old service pools are not removed until after a refresh cycle. -* :cccl-issue:`208` - Address compatibility for BIG-IP v13.0 Health Monitor interval and timeout. +* (github-517)Controller deletes SSL profiles off of Ingress virtual servers if watching multiple namespaces. +* (github-471)When updating routes, old service pools are not removed until after a refresh cycle. +* (github-228)Address compatibility for BIG-IP v13.0 Health Monitor interval and timeout. v1.4.0 ------ @@ -479,12 +508,12 @@ Added Functionality Bug Fixes ````````` -* :issues:`341` - HTTPS redirect applies to individual Routes instead of all Routes. -* :issues:`344` - Create default for SNI profile when using Ingress custom profiles from Secrets. -* :issues:`460` - Remove risk that pools will update with wrong members after a node update (NodePort mode). -* :issues:`428` - Controller writes unnecessary updates when no config changes occurred. -* :issues:`506` - Controller stops updating BIG-IP after an exception occurs in the python driver. -* :cccl-issue:`198` - Corrected a comparison problem in CCCL that caused unnecessary updates for BIG-IP Virtual Server resources. +* (github-341)HTTPS redirect applies to individual Routes instead of all Routes. +* (github-344)Create default for SNI profile when using Ingress custom profiles from Secrets. +* (github-460)Remove risk that pools will update with wrong members after a node update (NodePort mode). +* (github-428)Controller writes unnecessary updates when no config changes occurred. +* (github-506)Controller stops updating BIG-IP after an exception occurs in the python driver. +* (github-198)Corrected a comparison problem in CCCL that caused unnecessary updates for BIG-IP Virtual Server resources. Limitations ``````````` @@ -499,7 +528,7 @@ Limitations - `Download and install the latest iApps templates`_. - `Set the service to use the newer iApp template`_. -* Check BIG-IP version compatibility on Application Services (iApps) before deploying. See Application Services Integration iApp `[#16] `_ for more information. +* Check BIG-IP version compatibility on Application Services (iApps) before deploying. See Application Services Integration iApp. * Cannot delete ARP entries on BIG-IP v11.6.1 when running the Controller in Kubernetes with Flannel VXLAN enabled. * The controller will exit at startup if it cannot establish a connection with the BIG-IP. @@ -553,17 +582,16 @@ Bug Fixes Limitations ``````````` -* OpenShift - Does not currently support redirect for individual Routes. If a Route specifies +* OpenShift - (github-341)Does not currently support redirect for individual Routes. If a Route specifies "insecureEdgeTerminationPolicy" as "Redirect", the http virtual server will enable this policy for all Routes. - `[#341] `_ v1.1.1 ------ Bug Fixes ````````` -* Fix SIGSEV on non-"f5" valued class annotation `[#311] `_ -* Remove default pool for Ingress and Routes `[#288] `_ +* (github-311)Fix SIGSEV on non-"f5" valued class annotation. +* (github-288)Remove default pool for Ingress and Routes. v1.1.0 ------ @@ -623,4 +651,4 @@ Limitations .. _Download and install the latest iApps templates: https://support.f5.com/csp/article/K13422 -.. _Set the service to use the newer iApp template: https://support.f5.com/csp/article/K17001 +.. _Set the service to use the newer iApp template: https://support.f5.com/csp/article/K17001 \ No newline at end of file diff --git a/docs/_static/config_examples/crd/CustomResource.md b/docs/_static/config_examples/crd/CustomResource.md index 47d18dcc4..f8439a3ca 100644 --- a/docs/_static/config_examples/crd/CustomResource.md +++ b/docs/_static/config_examples/crd/CustomResource.md @@ -1,4 +1,4 @@ -# Container Ingress Services using Virtual Server Custom Resource +# Custom Resource Definitions This page is created to document the behaviour of CIS in CRD Mode(ALPHA Release). This is an ALPHA release which supports limited features. Check for the Supported Features and TO BE Implemented sections to understand in detail about the features. @@ -13,7 +13,7 @@ This page is created to document the behaviour of CIS in CRD Mode(ALPHA Release) * CIS supports 2 Custom Resources at this point of time. - VirtualServer - TLSProfile - + ## VirtualServer * VirtualServer resource defines load balancing configuration for a domain name. @@ -33,6 +33,13 @@ This page is created to document the behaviour of CIS in CRD Mode(ALPHA Release) servicePort: 80 ``` +## Label +* CIS will only process custom resources with f5cr Label as true. +``` + labels: + f5cr: "true" +``` + **Note: The above VirtualServer is insecure, Attach a TLSProfile to make it secure** ## TLSProfile @@ -84,6 +91,7 @@ This page is created to document the behaviour of CIS in CRD Mode(ALPHA Release) different terminations(for same domain), one with edge and another with re-encrypt. Todo this he needs to create two VirtualServers one with edge TLSProfile and another with re-encrypt TLSProfile. - Both the VirutalServers should be created with same virtualServerAddress * Single or Group of VirtualServers(with same virtualServerAddress) will be created as one common BIG-IP-VirtualServer. +* If user want to update secure virtual (TLS Virtual) server to insecure virtual (non-TLS server) server. User needs to delete the secure virtual server first and create a new virtual server. ## How CIS works with CRDs @@ -100,7 +108,8 @@ different terminations(for same domain), one with edge and another with re-encry # VirtualServer * Schema Validation - OpenAPI Schema Validation - https://raw.githubusercontent.com/F5Networks/k8s-bigip-ctlr/master/docs/_static/config_examples/crd/basic/vs-customresourcedefinitions.yml + + https://raw.githubusercontent.com/F5Networks/k8s-bigip-ctlr/master/docs/_static/config_examples/crd/basic/vs-customresourcedefinition.yml **VirtualServer Components** @@ -132,10 +141,13 @@ different terminations(for same domain), one with edge and another with re-encry | interval | Int | required | 5 | Seconds between health queries | | timeout | Int | Optional | 16 | Seconds before query fails | + **Note: Health Monitor associated with the first path will be considere if multiple path has same backend** + ## TLSProfile * Schema Validation - OpenAPI Schema Validation - https://raw.githubusercontent.com/F5Networks/k8s-bigip-ctlr/master/docs/_static/config_examples/crd/tls/tls-customresourcedefinitions.yml + + https://raw.githubusercontent.com/F5Networks/k8s-bigip-ctlr/master/docs/_static/config_examples/crd/tls/tls-customresourcedefinition.yml **TLSProfile Components** @@ -203,8 +215,11 @@ kubectl create -f sample-nodeport-k8s-bigip-ctlr-crd-secret.yml [-n kube-system] kubectl create -f sample-cluster-k8s-bigip-ctlr-crd-secret.yml [-n kube-system] ``` +## Examples + + https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/_static/config_examples/crd + ## To Be Implemented -* TLSProfile Support with k8s secrets * A/B Deployment * Support for WAF * Rewrite Rules @@ -212,5 +227,5 @@ kubectl create -f sample-cluster-k8s-bigip-ctlr-crd-secret.yml [-n kube-system] ## Note * “--custom-resource-mode=true” deploys CIS in Custom Resource Mode. -* CIS does not watch for ingress/routes when deployed in CRD Mode. -* CIS does not support combination of CRDs with any of Ingress/Routes or Configmaps. \ No newline at end of file +* CIS does not watch for ingress/routes/configmaps when deployed in CRD Mode. +* CIS does not support combination of CRDs with any of Ingress/Routes and Configmaps. \ No newline at end of file diff --git a/docs/_static/config_examples/crd/Install/README.md b/docs/_static/config_examples/crd/Install/README.md new file mode 100644 index 000000000..9c6d5e1ce --- /dev/null +++ b/docs/_static/config_examples/crd/Install/README.md @@ -0,0 +1,3 @@ +# Installation + +This section demonstrates the Installation of CIS in CRD Mode. \ No newline at end of file diff --git a/docs/_static/config_examples/crd/Install/customresourcedefinitions.yml b/docs/_static/config_examples/crd/Install/customresourcedefinitions.yml index a0aaa02f4..85f826191 100644 --- a/docs/_static/config_examples/crd/Install/customresourcedefinitions.yml +++ b/docs/_static/config_examples/crd/Install/customresourcedefinitions.yml @@ -28,6 +28,8 @@ spec: pattern: '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' httpTraffic: type: string + tlsProfileName: + type: string pools: type: array items: @@ -60,6 +62,10 @@ spec: type: integer timeout: type: integer + required: + - type + - send + - interval virtualServerAddress: type: string pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' @@ -110,4 +116,4 @@ spec: reference: type: string required: - - clientSSL \ No newline at end of file + - clientSSL diff --git a/docs/_static/config_examples/crd/basic/README.md b/docs/_static/config_examples/crd/basic/README.md new file mode 100644 index 000000000..a2e967307 --- /dev/null +++ b/docs/_static/config_examples/crd/basic/README.md @@ -0,0 +1,13 @@ +# UnSecure Virtual Server + +This section demonstrates the deployment of unsecured Virtual Servers. + +## example-single-pool-virtual.yaml + +By deploying this yaml file in your cluster, CIS will create a Virtual Server on BIG-IP with VIP "172.16.3.4". +It will load balance the traffic for domain cafe.example.com + +## example-two-pool-two-virtual.yaml + +By deploying this yaml file in your cluster, CIS will create two Virtual Servers on BIG-IP with VIP "172.16.3.4" and "172.16.3.5". +Former will load balance the traffic for domain coffee.example.com and later will load balance the traffic for domain tea-virtual-server diff --git a/docs/_static/config_examples/crd/basic/example-single-pool-virtual.yml b/docs/_static/config_examples/crd/basic/example-single-pool-virtual.yaml similarity index 66% rename from docs/_static/config_examples/crd/basic/example-single-pool-virtual.yml rename to docs/_static/config_examples/crd/basic/example-single-pool-virtual.yaml index 5206274e5..826fc68fa 100644 --- a/docs/_static/config_examples/crd/basic/example-single-pool-virtual.yml +++ b/docs/_static/config_examples/crd/basic/example-single-pool-virtual.yaml @@ -5,6 +5,8 @@ metadata: labels: f5cr: "true" spec: + # This is an insecure virtual, Please use TLSProfile to secure the virtual + # check out tls examples to understand more. host: cafe.example.com virtualServerAddress: "172.16.3.4" pools: diff --git a/docs/_static/config_examples/crd/basic/example-two-pool-two-virtual.yaml b/docs/_static/config_examples/crd/basic/example-two-pool-two-virtual.yaml index 9ff5c2beb..bf28e4c28 100644 --- a/docs/_static/config_examples/crd/basic/example-two-pool-two-virtual.yaml +++ b/docs/_static/config_examples/crd/basic/example-two-pool-two-virtual.yaml @@ -5,6 +5,8 @@ metadata: labels: f5cr: "true" spec: + # This is an insecure virtual, Please use TLSProfile to secure the virtual + # check out tls examples to understand more. virtualServerAddress: "172.16.3.4" host: coffee.example.com pools: @@ -23,6 +25,8 @@ metadata: labels: f5cr: "true" spec: + # This is an insecure virtual, Please use TLSProfile to secure the virtual + # check out tls examples to understand more. virtualServerAddress: "172.16.3.5" host: tea.example.com pools: diff --git a/docs/_static/config_examples/crd/basic/vs-customresourcedefinition.yml b/docs/_static/config_examples/crd/basic/vs-customresourcedefinition.yml index 0c79e0aa7..84c9e3764 100644 --- a/docs/_static/config_examples/crd/basic/vs-customresourcedefinition.yml +++ b/docs/_static/config_examples/crd/basic/vs-customresourcedefinition.yml @@ -60,6 +60,10 @@ spec: type: integer timeout: type: integer + required: + - type + - send + - interval virtualServerAddress: type: string pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' diff --git a/docs/_static/config_examples/crd/tls/README.md b/docs/_static/config_examples/crd/tls/README.md new file mode 100644 index 000000000..7a5c5d781 --- /dev/null +++ b/docs/_static/config_examples/crd/tls/README.md @@ -0,0 +1,3 @@ +# Various TLS Configurations + +This section demonstrates various examples of securing Virtual Servers with TLSProfiles. \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/README.md b/docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/README.md new file mode 100644 index 000000000..d3ab8fe17 --- /dev/null +++ b/docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/README.md @@ -0,0 +1,13 @@ +# Secure Virtual Server with Re-encrypt Termination using BIG-IP Profiles + +This section demonstrates the deployment of a Secure Virtual Server with Re-encrypt Termination using BIG-IP Profiles. + +## virtualserver.yml + +By deploying this yaml file in your cluster, CIS will create a Virtual Server on BIG-IP with VIP "172.16.3.5". +It will load balance the traffic for domain coffee.example.com + +## reencrypt-tls.yml + +By deploying this yaml file in your cluster, CIS will attach /Common/clientssl as clientssl and /Common/serverssl as serverssl +for above Virtual Server with VIP "172.16.3.5". \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/tls/reencrypt-tls.yml b/docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/reencrypt-tls.yml similarity index 65% rename from docs/_static/config_examples/crd/tls/tls/reencrypt-tls.yml rename to docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/reencrypt-tls.yml index c89b90fe1..5c9e81a1b 100644 --- a/docs/_static/config_examples/crd/tls/tls/reencrypt-tls.yml +++ b/docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/reencrypt-tls.yml @@ -7,8 +7,8 @@ metadata: spec: tls: termination: reencrypt - clientSSL: /common/clientssl - serverSSL: /common/serverssl + clientSSL: /Common/clientssl + serverSSL: /Common/serverssl reference: bigip hosts: - - coffee.example.com \ No newline at end of file + - coffee.example.com diff --git a/docs/_static/config_examples/crd/tls/tls/virtualserver.yml b/docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/virtualserver.yml similarity index 100% rename from docs/_static/config_examples/crd/tls/tls/virtualserver.yml rename to docs/_static/config_examples/crd/tls/reencrypt-bigip-reference/virtualserver.yml diff --git a/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/README.md b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/README.md new file mode 100644 index 000000000..b641fe370 --- /dev/null +++ b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/README.md @@ -0,0 +1,29 @@ +# Single Domain with combination of Edge and Re-encrypt termination + +This section demonstrates the deployment of two Virtual Servers one with Edge Termination and other with Re-encrypt Termination. +Both the Virtual Servers refer same domain[tea.example.com]. + +## tea-virtual-server_edge.yml + +By deploying this yaml file in your cluster, CIS will create a Virtual Server on BIG-IP with VIP "172.16.3.4". +It will load balance the traffic for service svc-edge on domain tea.example.com + +## tea-virtual-server_reen.yml + +By deploying this yaml file in your cluster, CIS will update Virtual Server on BIG-IP with VIP "172.16.3.4". +It will load balance the traffic for service svc-1 and svc-2 on domain tea.example.com + +## reencrypt-tls.yml + +By deploying this yaml file in your cluster, CIS will attach k8s secrets[clientssl and serverssl] as client and server +profiles for VIP "172.16.3.4". + +This is only applicable for services svc-1 and svc-2 + +## edge-tls.yml + +By deploying this yaml file in your cluster, CIS will attach k8s secret[clientssl] as client profile for VIP "172.16.3.4". + +This is only applicable for services svc-edge + +## Note: clientssl mentioned in both edge-tls.yml and reencrypt-tls.yml should be same as both are pointing to same domain. \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/edge-tls.yaml b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/edge-tls.yaml new file mode 100644 index 000000000..3297556c4 --- /dev/null +++ b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/edge-tls.yaml @@ -0,0 +1,14 @@ +apiVersion: cis.f5.com/v1 +kind: TLSProfile +metadata: + labels: + f5cr: "true" + name: edge-tls + namespace: default +spec: + hosts: + - tea.example.com + tls: + clientSSL: clientssl + reference: secret + termination: edge \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/reencrypt-tls.yml b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/reencrypt-tls.yml new file mode 100644 index 000000000..32c3261cd --- /dev/null +++ b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/reencrypt-tls.yml @@ -0,0 +1,14 @@ +apiVersion: cis.f5.com/v1 +kind: TLSProfile +metadata: + name: reencrypt-tls + labels: + f5cr: "true" +spec: + tls: + termination: reencrypt + clientSSL: clientssl + serverSSL: serverssl + reference: secret + hosts: + - tea.example.com \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/tea-virtual-server_edge.yaml b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/tea-virtual-server_edge.yaml new file mode 100644 index 000000000..6685250cd --- /dev/null +++ b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/tea-virtual-server_edge.yaml @@ -0,0 +1,16 @@ +apiVersion: cis.f5.com/v1 +kind: VirtualServer +metadata: + labels: + f5cr: "true" + name: tea-virtual-server-edge + namespace: default +spec: + host: tea.example.com + httpTraffic: redirect + pools: + - path: /neam + service: svc-edge + servicePort: 80 + tlsProfileName: edge-tls + virtualServerAddress: 172.16.3.4 \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/tea-virtual-server_reen.yaml b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/tea-virtual-server_reen.yaml new file mode 100644 index 000000000..dabd7feea --- /dev/null +++ b/docs/_static/config_examples/crd/tls/singledomain-with-edge-reencrypt-combination/tea-virtual-server_reen.yaml @@ -0,0 +1,19 @@ +apiVersion: cis.f5.com/v1 +kind: VirtualServer +metadata: + labels: + f5cr: "true" + name: tea-virtual-server-reen + namespace: default +spec: + host: tea.example.com + httpTraffic: redirect + pools: + - path: /green + service: svc-1 + servicePort: 80 + - path: /black + service: svc-2 + servicePort: 80 + tlsProfileName: reencrypt-tls + virtualServerAddress: 172.16.3.4 \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/tls-with-health-monitor/README.md b/docs/_static/config_examples/crd/tls/tls-with-health-monitor/README.md new file mode 100644 index 000000000..e70b2e724 --- /dev/null +++ b/docs/_static/config_examples/crd/tls/tls-with-health-monitor/README.md @@ -0,0 +1,3 @@ +# Health Monitor + +This section demonstrates the deployment of Virtual Servers with Health Monitor. \ No newline at end of file diff --git a/docs/_static/config_examples/crd/tls/tls-with-httpredirect/README.md b/docs/_static/config_examples/crd/tls/tls-with-httpredirect/README.md new file mode 100644 index 000000000..701e3e6ad --- /dev/null +++ b/docs/_static/config_examples/crd/tls/tls-with-httpredirect/README.md @@ -0,0 +1,9 @@ +# Configure behaviour of HTTP Virtual Server + +This section demonstrates how to Configure behaviour HTTP Virtual Server. + +``` +// httpTraffic = allow -> Allows HTTP +// httpTraffic = none -> Only HTTPS +// httpTraffic = redirect -> redirects HTTP to HTTPS +``` \ No newline at end of file diff --git a/next-version.txt b/next-version.txt index 7ec1d6db4..7c3272873 100644 --- a/next-version.txt +++ b/next-version.txt @@ -1 +1 @@ -2.1.0 +2.1.1 \ No newline at end of file diff --git a/operator/build/Dockerfile b/operator/build/Dockerfile index 82d44bcae..8836f7773 100644 --- a/operator/build/Dockerfile +++ b/operator/build/Dockerfile @@ -3,7 +3,7 @@ FROM quay.io/operator-framework/helm-operator:latest ### Required OpenShift Labels LABEL name="F5 BIG-IP Controller Operator" \ vendor="F5 Networks Inc" \ - version="v1.1.0" \ + version="v1.2.0" \ release="1" \ summary="F5 BIG-IP Controller Operator" \ description="This operator will deploy F5 BIG-IP Controller for Kubernetes and OpenShift into the cluster." diff --git a/operator/deploy/crds/cis.f5.com_v1_f5bigipctlr_cr.yaml b/operator/deploy/crds/cis.f5.com_v1_f5bigipctlr_cr.yaml index 811d02196..78d229714 100644 --- a/operator/deploy/crds/cis.f5.com_v1_f5bigipctlr_cr.yaml +++ b/operator/deploy/crds/cis.f5.com_v1_f5bigipctlr_cr.yaml @@ -3,7 +3,7 @@ kind: F5BigIpCtlr metadata: name: f5-server spec: - version: 2.0.0 + version: 2.1.0 args: log_as3_response: true manage_routes: true diff --git a/operator/helm-charts/f5-bigip-ctlr/Chart.yaml b/operator/helm-charts/f5-bigip-ctlr/Chart.yaml index a0c755582..6ae41bff7 100644 --- a/operator/helm-charts/f5-bigip-ctlr/Chart.yaml +++ b/operator/helm-charts/f5-bigip-ctlr/Chart.yaml @@ -1,5 +1,4 @@ apiVersion: v1 -description: Deploy the F5 Networks BIG-IP Controller for Kubernetes and OpenShift - (k8s-bigip-ctlr). +description: Deploy the F5 Networks BIG-IP Controller for Kubernetes and OpenShift (k8s-bigip-ctlr). name: f5-bigip-ctlr -version: 0.0.7 +version: 0.0.8 diff --git a/operator/helm-charts/f5-bigip-ctlr/templates/_helpers.tpl b/operator/helm-charts/f5-bigip-ctlr/templates/_helpers.tpl index ff3a4b2b2..7ce05d2ef 100644 --- a/operator/helm-charts/f5-bigip-ctlr/templates/_helpers.tpl +++ b/operator/helm-charts/f5-bigip-ctlr/templates/_helpers.tpl @@ -6,6 +6,28 @@ Expand the name of the chart. {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "deployment.apiVersion" -}} +{{- if semverCompare ">=1.9-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "apps/v1" -}} +{{- else -}} +{{- print "extensions/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Check for user given namespace or give kube-system +*/}} +{{- define "f5-bigip-ctlr.namespace" -}} +{{- if hasKey .Values "namespace" -}} +{{- .Values.namespace -}} +{{- else -}} +{{- print "kube-system" -}} +{{- end -}} +{{- 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). @@ -23,7 +45,6 @@ If release name contains chart name it will be used as a full name. {{- end -}} {{- end -}} {{- end -}} - {{/* Create chart name and version as used by the chart label. */}} @@ -31,18 +52,7 @@ Create chart name and version as used by the chart label. {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- end -}} -{{/* -Return the appropriate apiVersion for deployment. -*/}} -{{- define "deployment.apiVersion" -}} -{{- if semverCompare ">=1.9-0" .Capabilities.KubeVersion.GitVersion -}} -{{- print "apps/v1" -}} -{{- else -}} -{{- print "extensions/v1beta1" -}} -{{- end -}} -{{- end -}} - -{{/* + {{/* Create the name of the service account to use */}} {{- define "f5-bigip-ctlr.serviceAccountName" -}} @@ -52,14 +62,3 @@ Create the name of the service account to use {{ default "default" .Values.serviceAccount.name }} {{- end -}} {{- end -}} - -{{/* -Check for user given namespace or give kube-system -*/}} -{{- define "f5-bigip-ctlr.namespace" -}} -{{- if hasKey .Values "namespace" -}} -{{- .Values.namespace -}} -{{- else -}} -{{- print "kube-system" -}} -{{- end -}} -{{- end -}} diff --git a/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-clusterrole.yaml b/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-clusterrole.yaml index 0b30d0738..bd44d3249 100644 --- a/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-clusterrole.yaml +++ b/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-clusterrole.yaml @@ -44,4 +44,14 @@ rules: - events - ingresses/status - routes/status + - verbs: + - get + - list + - watch + - update + apiGroups: + - cis.f5.com + resources: + - virtualservers + - tlsprofiles {{- end -}} diff --git a/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-customresourcedefinitions.yml b/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-customresourcedefinitions.yml new file mode 100644 index 000000000..3b67d1d3c --- /dev/null +++ b/operator/helm-charts/f5-bigip-ctlr/templates/f5-bigip-ctlr-customresourcedefinitions.yml @@ -0,0 +1,115 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: virtualservers.cis.f5.com +spec: + group: cis.f5.com + names: + kind: VirtualServer + plural: virtualservers + shortNames: + - vs + singular: virtualserver + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + host: + type: string + pattern: '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + httpTraffic: + type: string + tlsProfileName: + type: string + pools: + type: array + items: + type: object + properties: + path: + type: string + pattern: '^\/([A-z0-9-_+]+\/)*([A-z0-9]+\/?)*$' + service: + type: string + pattern: '^([A-z0-9-_+])*([A-z0-9])$' + nodeMemberLabel: + type: string + pattern: '^[a-zA-Z0-9][-A-Za-z0-9_.]{0,61}[a-zA-Z0-9]=[a-zA-Z0-9][-A-Za-z0-9_.]{0,61}[a-zA-Z0-9]$' + servicePort: + type: integer + minimum: 1 + maximum: 65535 + monitor: + type: object + properties: + type: + type: string + enum: [http, https] + send: + type: string + recv: + type: string + interval: + type: integer + timeout: + type: integer + virtualServerAddress: + type: string + pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' + required: + - virtualServerAddress + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: tlsprofiles.cis.f5.com +spec: + group: cis.f5.com + names: + kind: TLSProfile + plural: tlsprofiles + shortNames: + - tls + singular: tlsprofile + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + hosts: + type: array + items: + type: string + pattern: '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + tls: + type: object + properties: + termination: + type: string + enum: [edge, reencrypt, passthrough] + clientSSL: + type: string + serverSSL: + type: string + reference: + type: string + required: + - clientSSL \ No newline at end of file diff --git a/operator/manifest/f5-bundle.zip b/operator/manifest/f5-bundle.zip new file mode 100644 index 000000000..575aeafde Binary files /dev/null and b/operator/manifest/f5-bundle.zip differ diff --git a/operator/manifest/f5-bigip-ctlr-operator.v1.1.0.clusterserviceversion.yaml b/operator/manifest/f5-bundle/1.1.0/f5-bigip-ctlr-operator.v1.1.0.clusterserviceversion.yaml similarity index 99% rename from operator/manifest/f5-bigip-ctlr-operator.v1.1.0.clusterserviceversion.yaml rename to operator/manifest/f5-bundle/1.1.0/f5-bigip-ctlr-operator.v1.1.0.clusterserviceversion.yaml index e8b6d5387..674358ea6 100644 --- a/operator/manifest/f5-bigip-ctlr-operator.v1.1.0.clusterserviceversion.yaml +++ b/operator/manifest/f5-bundle/1.1.0/f5-bigip-ctlr-operator.v1.1.0.clusterserviceversion.yaml @@ -8,7 +8,7 @@ metadata: [{"apiVersion":"cis.f5.com/v1","kind":"F5BigIpCtlr","metadata":{"name":"f5-server"},"spec":{"args":{"log_as3_response":true,"manage_routes":true,"agent":"as3","log_level":"","route_vserver_addr":"","bigip_partition":"","openshift_sdn_name":"","bigip_url":"","insecure":true,"pool-member-type":""},"bigip_login_secret":"","image":{"pullPolicy":"Always","repo":"k8s-bigip-ctlr","user":"f5networks"},"namespace":"kube-system","rbac":{"create":true},"resources":{},"serviceAccount":{"create":true,"name":null},"version":"latest"}}] categories: Networking certified: 'false' - createdAt: '2020-02-07' + createdAt: '2020-07-30' description: >- Operator to install F5 Container Ingress Services (CIS) for BIG-IP. containerImage: 'registry.connect.redhat.com/f5networks/k8s-bigip-ctlr-operator:latest' @@ -53,7 +53,7 @@ spec: maturity: beta version: 1.1.0 replaces: '' - minKubeVersion: 2.0.0 + minKubeVersion: 1.13.0 keywords: - Ingress Controller - BIGIP diff --git a/operator/manifest/f5bigipctlrs.cis.f5.com.crd.yaml b/operator/manifest/f5-bundle/1.1.0/f5bigipctlrs.cis.f5.com.crd.yaml similarity index 100% rename from operator/manifest/f5bigipctlrs.cis.f5.com.crd.yaml rename to operator/manifest/f5-bundle/1.1.0/f5bigipctlrs.cis.f5.com.crd.yaml diff --git a/operator/manifest/f5-bundle/1.2.0/f5-bigip-ctlr-operator.v1.2.0.clusterserviceversion.yaml b/operator/manifest/f5-bundle/1.2.0/f5-bigip-ctlr-operator.v1.2.0.clusterserviceversion.yaml new file mode 100644 index 000000000..4e6bb0681 --- /dev/null +++ b/operator/manifest/f5-bundle/1.2.0/f5-bigip-ctlr-operator.v1.2.0.clusterserviceversion.yaml @@ -0,0 +1,252 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + name: f5-bigip-ctlr-operator.v1.2.0 + namespace: placeholder + annotations: + alm-examples: >- + [{"apiVersion":"cis.f5.com/v1","kind":"F5BigIpCtlr","metadata":{"name":"f5-server"},"spec":{"args":{"log_as3_response":true,"manage_routes":true,"agent":"as3","log_level":"","route_vserver_addr":"","bigip_partition":"","openshift_sdn_name":"","bigip_url":"","insecure":true,"pool-member-type":""},"bigip_login_secret":"","image":{"pullPolicy":"Always","repo":"k8s-bigip-ctlr","user":"f5networks"},"namespace":"kube-system","rbac":{"create":true},"resources":{},"serviceAccount":{"create":true,"name":null},"version":"latest"}}] + categories: Networking + certified: 'false' + createdAt: '2020-08-21' + description: >- + Operator to install F5 Container Ingress Services (CIS) for BIG-IP. + containerImage: 'registry.connect.redhat.com/f5networks/k8s-bigip-ctlr-operator:latest' + support: F5 Operators Team + capabilities: Basic Install + repository: 'https://github.com/F5Networks/k8s-bigip-ctlr' +spec: + displayName: 'F5 Container Ingress Services' + description: > + ## Introduction + + This Operator installs F5 Container Ingress Services (CIS) for BIG-IP in + your Cluster. This enables to configure and deploy CIS using Helm Charts. + + ## F5 Container Ingress Services for BIG-IP + + F5 Container Ingress Services (CIS) integrates with container orchestration + environments to dynamically create L4/L7 services on F5 BIG-IP systems, and + load balance network traffic across the services. + + Monitoring the orchestration API server, CIS is able to modify the BIG-IP + system configuration based on changes made to containerized applications. + + ## Documentation + + Refer to F5 documentation + + - CIS on OpenShift (https://clouddocs.f5.com/containers/latest/userguide/openshift/) + - OpenShift Routes (https://clouddocs.f5.com/containers/latest/userguide/routes.html) + + ## Prerequisites + + Create BIG-IP login credentials for use with Operator Helm charts. A basic + way be, + + ``` + + oc create secret generic -n kube-system + --from-literal=username= --from-literal=password= + + ``` + maturity: beta + version: 1.2.0 + replaces: '' + minKubeVersion: 1.13.0 + keywords: + - Ingress Controller + - BIGIP + - F5 + - container + - router + - application + - delivery + - controller + - waf + - firewall + - loadbalancer + maintainers: + - name: F5 Operators Team + email: f5_cis_operators@f5.com + provider: + name: F5 Networks Inc. + labels: {} + selector: + matchLabels: {} + links: + - name: Documentation + url: 'https://clouddocs.f5.com/containers/latest/' + - name: Github Repo + url: 'https://github.com/F5Networks/k8s-bigip-ctlr/operator' + icon: + - base64data: >- + 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 + mediatype: image/png + customresourcedefinitions: + owned: + - name: f5bigipctlrs.cis.f5.com + displayName: F5BigIpCtlr + kind: F5BigIpCtlr + version: v1 + description: >- + This CRD provides kind `F5BigIpCtlr` to configure and deploy F5 BIG-IP + Controller. + resources: + - version: v1 + kind: Deployment + - version: v1 + kind: Service + - version: v1 + kind: ReplicaSet + - version: v1 + kind: Pod + - version: v1 + kind: Secret + - version: v1 + kind: ConfigMap + specDescriptors: + - description: Version is a read-only field. It contains the current version of F5 BIG-IP Controller Operator. + displayName: Version + path: version + statusDescriptors: + - path: phase + displayName: Status + description: Status of the F5 Container Ingress Services Operator. + x-descriptors: + - 'urn:alm:descriptor:io.kubernetes.phase' + required: [] + install: + strategy: deployment + spec: + clusterPermissions: + - serviceAccountName: f5-bigip-ctlr-operator + rules: + - apiGroups: + - '' + resources: + - pods + - services + - services/finalizers + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + - serviceaccounts + verbs: + - '*' + - apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - '*' + - apiGroups: + - '' + resources: + - namespaces + verbs: + - '*' + - apiGroups: + - '' + resources: + - configmaps + - secrets + verbs: + - '*' + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create + - apiGroups: + - apps + resourceNames: + - f5-bigip-ctlr-operator + resources: + - deployments/finalizers + verbs: + - update + - apiGroups: + - '' + resources: + - pods + verbs: + - get + - apiGroups: + - apps + resources: + - replicasets + - deployments + verbs: + - get + - apiGroups: + - cis.f5.com + resources: + - '*' + verbs: + - '*' + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + - roles + - rolebindings + verbs: + - '*' + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - '*' + - apiGroups: + - charts.helm.k8s.io + resources: + - '*' + verbs: + - '*' + deployments: + - name: f5-bigip-ctlr-operator + spec: + replicas: 1 + selector: + matchLabels: + name: f5-bigip-ctlr-operator + template: + metadata: + labels: + name: f5-bigip-ctlr-operator + spec: + serviceAccountName: f5-bigip-ctlr-operator + containers: + - name: f5-bigip-ctlr-operator + image: registry.connect.redhat.com/f5networks/k8s-bigip-ctlr-operator:latest + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.annotations['olm.targetNamespaces'] + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: f5-bigip-ctlr-operator + installModes: + - type: OwnNamespace + supported: true + - type: SingleNamespace + supported: true + - type: MultiNamespace + supported: true + - type: AllNamespaces + supported: true diff --git a/operator/manifest/f5-bundle/1.2.0/f5bigipctlrs.cis.f5.com.crd.yaml b/operator/manifest/f5-bundle/1.2.0/f5bigipctlrs.cis.f5.com.crd.yaml new file mode 100644 index 000000000..9e6743024 --- /dev/null +++ b/operator/manifest/f5-bundle/1.2.0/f5bigipctlrs.cis.f5.com.crd.yaml @@ -0,0 +1,19 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: f5bigipctlrs.cis.f5.com +spec: + group: cis.f5.com + names: + kind: F5BigIpCtlr + listKind: F5BigIpCtlrList + plural: f5bigipctlrs + singular: f5bigipctlr + scope: Namespaced + subresources: + status: {} + version: v1 + versions: + - name: v1 + served: true + storage: true diff --git a/operator/manifest/f5-bigip-ctlr-operator.package.yaml b/operator/manifest/f5-bundle/f5-bigip-ctlr-operator.package.yaml similarity index 57% rename from operator/manifest/f5-bigip-ctlr-operator.package.yaml rename to operator/manifest/f5-bundle/f5-bigip-ctlr-operator.package.yaml index b32cbb26e..3c60ee753 100644 --- a/operator/manifest/f5-bigip-ctlr-operator.package.yaml +++ b/operator/manifest/f5-bundle/f5-bigip-ctlr-operator.package.yaml @@ -1,4 +1,4 @@ packageName: f5-bigip-ctlr-operator channels: - name: beta - currentCSV: f5-bigip-ctlr-operator.v1.1.0 + currentCSV: f5-bigip-ctlr-operator.v1.2.0 diff --git a/operator/manifest/operator-latest.zip b/operator/manifest/operator-latest.zip deleted file mode 100644 index 80066015f..000000000 Binary files a/operator/manifest/operator-latest.zip and /dev/null differ diff --git a/pkg/agent/as3/as3Common.go b/pkg/agent/as3/as3Common.go index 74b20fdad..9f972df0f 100644 --- a/pkg/agent/as3/as3Common.go +++ b/pkg/agent/as3/as3Common.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/as3/as3ConfigMap.go b/pkg/agent/as3/as3ConfigMap.go index 98f92bc15..6f8623037 100644 --- a/pkg/agent/as3/as3ConfigMap.go +++ b/pkg/agent/as3/as3ConfigMap.go @@ -53,6 +53,8 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() ( if am.as3Validation == true { if ok := am.validateAS3Template(rscCfgMap.Data); !ok { log.Errorf("[AS3] Error validating AS3 template") + log.Errorf("[AS3] Error in processing the ConfigMap: %v/%v", + rscCfgMap.Namespace, rscCfgMap.Name) continue } } @@ -124,6 +126,8 @@ func (am *AS3Manager) processCfgMap(rscCfgMap *AgentCfgMap) ( obj, ok := getAS3ObjectFromTemplate(as3Tmpl) if !ok { log.Errorf("[AS3] Error processing AS3 template") + log.Errorf("[AS3] Error in processing the ConfigMap: %v/%v", + rscCfgMap.Namespace, rscCfgMap.Name) return nil, nil } diff --git a/pkg/agent/as3/as3Manager.go b/pkg/agent/as3/as3Manager.go index 1840e45e9..8957b9157 100644 --- a/pkg/agent/as3/as3Manager.go +++ b/pkg/agent/as3/as3Manager.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,10 +30,14 @@ import ( ) const ( - svcTenantLabel = "cis.f5.com/as3-tenant=" - svcAppLabel = "cis.f5.com/as3-app=" - svcPoolLabel = "cis.f5.com/as3-pool=" - as3SupportedVersion = 3.18 + svcTenantLabel = "cis.f5.com/as3-tenant=" + svcAppLabel = "cis.f5.com/as3-app=" + svcPoolLabel = "cis.f5.com/as3-pool=" + as3SupportedVersion = 3.18 + //Update as3Version,defaultAS3Version,defaultAS3Build while updating AS3 validation schema + as3Version = 3.21 + defaultAS3Version = "3.21.0" + defaultAS3Build = "4" as3tenant = "Tenant" as3class = "class" as3SharedApplication = "Shared" @@ -41,7 +45,7 @@ const ( as3shared = "shared" as3template = "template" //as3SchemaLatestURL = "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json" - as3SchemaFileName = "as3-schema-3.20.0-3-cis.json" + as3SchemaFileName = "as3-schema-3.21.0-4-cis.json" ) var baseAS3Config = `{ @@ -406,17 +410,25 @@ func (am *AS3Manager) IsBigIPAppServicesAvailable() error { return err } versionstr := version[:strings.LastIndex(version, ".")] - bigIPVersion, err := strconv.ParseFloat(versionstr, 64) + bigIPAS3Version, err := strconv.ParseFloat(versionstr, 64) if err != nil { log.Errorf("[AS3] Error while converting AS3 version to float") return err } - if bigIPVersion >= as3SupportedVersion { + if bigIPAS3Version >= as3SupportedVersion && bigIPAS3Version <= as3Version { log.Debugf("[AS3] BIGIP is serving with AS3 version: %v", version) return nil } + if bigIPAS3Version > as3Version { + am.as3Version = defaultAS3Version + as3Build := defaultAS3Build + am.as3Release = am.as3Version + "-" + as3Build + log.Debugf("[AS3] BIGIP is serving with AS3 version: %v", bigIPAS3Version) + return nil + } + return fmt.Errorf("CIS versions >= 2.0 are compatible with AS3 versions >= %v. "+ "Upgrade AS3 version in BIGIP from %v to %v or above.", as3SupportedVersion, - bigIPVersion, as3SupportedVersion) + bigIPAS3Version, as3SupportedVersion) } diff --git a/pkg/agent/as3/as3Manager_test.go b/pkg/agent/as3/as3Manager_test.go index 1d17cecc9..55e4bf4c0 100644 --- a/pkg/agent/as3/as3Manager_test.go +++ b/pkg/agent/as3/as3Manager_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package as3 import ( "encoding/json" + . "github.com/F5Networks/k8s-bigip-ctlr/pkg/resource" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -45,8 +46,8 @@ var _ = Describe("AS3Manager Tests", func() { var mockMgr *mockAS3Manager BeforeEach(func() { mockMgr = newMockAS3Manager(&Params{ - As3Version: "3.20.0", - As3Release: "3.20.0-3", + As3Version: "3.21.0", + As3Release: "3.21.0-4", }) }) AfterEach(func() { diff --git a/pkg/agent/as3/as3Resource.go b/pkg/agent/as3/as3Resource.go index 2a85dc439..d6a719541 100644 --- a/pkg/agent/as3/as3Resource.go +++ b/pkg/agent/as3/as3Resource.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/as3/as3Types.go b/pkg/agent/as3/as3Types.go index f0c36b396..8a2824318 100644 --- a/pkg/agent/as3/as3Types.go +++ b/pkg/agent/as3/as3Types.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/as3/as3Utils.go b/pkg/agent/as3/as3Utils.go index abb0f1595..9fc166699 100644 --- a/pkg/agent/as3/as3Utils.go +++ b/pkg/agent/as3/as3Utils.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/as3/as3Utils_test.go b/pkg/agent/as3/as3Utils_test.go index 9c52d1349..7a9e9afc0 100644 --- a/pkg/agent/as3/as3Utils_test.go +++ b/pkg/agent/as3/as3Utils_test.go @@ -2,6 +2,7 @@ package as3 import ( "io/ioutil" + "sort" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -62,6 +63,7 @@ var _ = Describe("Tenant parsing in AS3 declaration", func() { It("Get Tenants from a declaration", func() { cmcfg1 := readConfigFile(configPath + "as3config_multi_cm_unified.json") tenants := getTenants(as3Declaration(cmcfg1), true) + sort.Strings(tenants) Expect(tenants).To(Equal([]string{"Tenant1", "Tenant2"}), "Failed to get tenants") }) }) diff --git a/pkg/agent/as3/l2l3agent.go b/pkg/agent/as3/l2l3agent.go index 95ee3e303..6b880181c 100644 --- a/pkg/agent/as3/l2l3agent.go +++ b/pkg/agent/as3/l2l3agent.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/as3/postManager.go b/pkg/agent/as3/postManager.go index ae3cf793d..131f347d4 100644 --- a/pkg/agent/as3/postManager.go +++ b/pkg/agent/as3/postManager.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/cccl/ccclManager.go b/pkg/agent/cccl/ccclManager.go index b6cdc52a6..6138d2c4a 100644 --- a/pkg/agent/cccl/ccclManager.go +++ b/pkg/agent/cccl/ccclManager.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/agent/cccl/outputConfig.go b/pkg/agent/cccl/outputConfig.go index 79ae9afe7..2f74ab04a 100644 --- a/pkg/agent/cccl/outputConfig.go +++ b/pkg/agent/cccl/outputConfig.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/agentResponseHandler.go b/pkg/appmanager/agentResponseHandler.go index 73d6e2d1e..c52228dbe 100644 --- a/pkg/appmanager/agentResponseHandler.go +++ b/pkg/appmanager/agentResponseHandler.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/appManager.go b/pkg/appmanager/appManager.go index 43c7716b2..d2a78705a 100644 --- a/pkg/appmanager/appManager.go +++ b/pkg/appmanager/appManager.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -2434,6 +2434,23 @@ func containsNode(nodes []Node, name string) bool { return false } +type byTimestamp []v1.Service + +//sort services by timestamp +func (slice byTimestamp) Len() int { + return len(slice) +} + +func (slice byTimestamp) Less(i, j int) bool { + d1 := slice[i].GetCreationTimestamp() + d2 := slice[j].GetCreationTimestamp() + return d1.Before(&d2) +} + +func (slice byTimestamp) Swap(i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} + // Performs Service discovery for the given AS3 Pool and returns a pool. // Service discovery is loosely coupled with Kubernetes Service labels. A Kubernetes Service is treated as a match for // an AS3 Pool, if the Kubernetes Service have the following labels and their values matches corresponding AS3 @@ -2461,14 +2478,16 @@ func (m *Manager) getEndpoints(selector, namespace string) []Member { } if len(services.Items) > 1 { - svcNames := "" + svcName := "" + sort.Sort(byTimestamp(services.Items)) + //picking up the oldest service + services.Items = services.Items[:1] for _, service := range services.Items { - svcNames += fmt.Sprintf("Service: %v, Namespace: %v \n", service.Name, service.Namespace) + svcName += fmt.Sprintf("Service: %v, Namespace: %v,Timestamp: %v\n", service.Name, service.Namespace, service.GetCreationTimestamp()) } - log.Errorf("[CORE] Multiple Services are tagged for this pool. Ignoring all endpoints.\n%v", svcNames) - return members + log.Warningf("[CORE] Multiple Services are tagged for this pool. Using oldest service endpoints.\n%v", svcName) } for _, service := range services.Items { diff --git a/pkg/appmanager/appManager_test.go b/pkg/appmanager/appManager_test.go index 96cbabc34..08e76c13d 100644 --- a/pkg/appmanager/appManager_test.go +++ b/pkg/appmanager/appManager_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/eventNotifier.go b/pkg/appmanager/eventNotifier.go index 8a9353761..781f5e3c5 100644 --- a/pkg/appmanager/eventNotifier.go +++ b/pkg/appmanager/eventNotifier.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ package appmanager import ( "sync" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" diff --git a/pkg/appmanager/eventNotifier_test.go b/pkg/appmanager/eventNotifier_test.go index af10ccd3d..30c2b02d6 100644 --- a/pkg/appmanager/eventNotifier_test.go +++ b/pkg/appmanager/eventNotifier_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package appmanager import ( "fmt" + "github.com/F5Networks/k8s-bigip-ctlr/pkg/agent/cccl" "github.com/F5Networks/k8s-bigip-ctlr/pkg/agent" @@ -27,7 +28,7 @@ import ( . "github.com/onsi/gomega" fakeRouteClient "github.com/openshift/client-go/route/clientset/versioned/fake" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/appmanager/healthMonitors.go b/pkg/appmanager/healthMonitors.go index 6b5a628ee..202a4e6b5 100644 --- a/pkg/appmanager/healthMonitors.go +++ b/pkg/appmanager/healthMonitors.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/healthMonitors_test.go b/pkg/appmanager/healthMonitors_test.go index ca3781daa..b431d0419 100644 --- a/pkg/appmanager/healthMonitors_test.go +++ b/pkg/appmanager/healthMonitors_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import ( routeapi "github.com/openshift/api/route/v1" fakeRouteClient "github.com/openshift/client-go/route/clientset/versioned/fake" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes/fake" diff --git a/pkg/appmanager/profiles_test.go b/pkg/appmanager/profiles_test.go index 4ca586b96..d73c588fb 100644 --- a/pkg/appmanager/profiles_test.go +++ b/pkg/appmanager/profiles_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,10 @@ package appmanager import ( "fmt" - "github.com/F5Networks/k8s-bigip-ctlr/pkg/agent/cccl" "sort" + "github.com/F5Networks/k8s-bigip-ctlr/pkg/agent/cccl" + "github.com/F5Networks/k8s-bigip-ctlr/pkg/agent" . "github.com/F5Networks/k8s-bigip-ctlr/pkg/resource" "github.com/F5Networks/k8s-bigip-ctlr/pkg/test" @@ -29,7 +30,7 @@ import ( routeapi "github.com/openshift/api/route/v1" fakeRouteClient "github.com/openshift/client-go/route/clientset/versioned/fake" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" diff --git a/pkg/appmanager/resourceConfig.go b/pkg/appmanager/resourceConfig.go index f5f3edb34..98c886641 100644 --- a/pkg/appmanager/resourceConfig.go +++ b/pkg/appmanager/resourceConfig.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/resourceConfig_test.go b/pkg/appmanager/resourceConfig_test.go index 9d0502996..15dc92cb3 100644 --- a/pkg/appmanager/resourceConfig_test.go +++ b/pkg/appmanager/resourceConfig_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/routing.go b/pkg/appmanager/routing.go index d8e0b3ff7..9627ec67f 100644 --- a/pkg/appmanager/routing.go +++ b/pkg/appmanager/routing.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/schema.go b/pkg/appmanager/schema.go index 85a0f32aa..f9232478c 100644 --- a/pkg/appmanager/schema.go +++ b/pkg/appmanager/schema.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/schema_test.go b/pkg/appmanager/schema_test.go index cd3111f7b..41cf16d20 100644 --- a/pkg/appmanager/schema_test.go +++ b/pkg/appmanager/schema_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/appmanager/validateResources.go b/pkg/appmanager/validateResources.go index dcccd6851..ec091afc5 100644 --- a/pkg/appmanager/validateResources.go +++ b/pkg/appmanager/validateResources.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/crmanager/backend.go b/pkg/crmanager/backend.go index 0c75339a3..66c66e6f4 100644 --- a/pkg/crmanager/backend.go +++ b/pkg/crmanager/backend.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -202,8 +202,9 @@ func processDataGroupForAS3(intDgMap InternalDataGroupMap, sharedApp as3Applicat for _, record := range dg.Records { var rec as3Record rec.Key = record.Name + virtualAddress := extractVirtualAddress(record.Data) // To override default Value created for CCCL for certain DG types - if val, ok := getDGRecordValueForAS3(idk.Name, sharedApp); ok { + if val, ok := getDGRecordValueForAS3(idk.Name, sharedApp, virtualAddress); ok { rec.Value = val } else { rec.Value = record.Data @@ -217,8 +218,9 @@ func processDataGroupForAS3(intDgMap InternalDataGroupMap, sharedApp as3Applicat for _, record := range dg.Records { var rec as3Record rec.Key = record.Name + virtualAddress := extractVirtualAddress(record.Data) // To override default Value created for CCCL for certain DG types - if val, ok := getDGRecordValueForAS3(idk.Name, sharedApp); ok { + if val, ok := getDGRecordValueForAS3(idk.Name, sharedApp, virtualAddress); ok { rec.Value = val } else { rec.Value = record.Data @@ -236,16 +238,25 @@ func processDataGroupForAS3(intDgMap InternalDataGroupMap, sharedApp as3Applicat } } -func getDGRecordValueForAS3(dgName string, sharedApp as3Application) (string, bool) { +func extractVirtualAddress(str string) string { + var address string + if strings.HasPrefix(str, "crd_") && strings.HasSuffix(str, "_tls_client") { + address = strings.ReplaceAll(strings.TrimRight(strings.TrimLeft(str, "crd_"), "_tls_client"), "_", ".") + } + return address +} + +func getDGRecordValueForAS3(dgName string, sharedApp as3Application, virtualAddress string) (string, bool) { switch dgName { case ReencryptServerSslDgName: for _, v := range sharedApp { - if svc, ok := v.(*as3Service); ok && svc.Class == "Service_HTTPS" { + if svc, ok := v.(*as3Service); ok && svc.Class == "Service_HTTPS" && + svc.VirtualAddresses[0] == virtualAddress { if val, ok := svc.ClientTLS.(*as3ResourcePointer); ok { return val.BigIP, true } if val, ok := svc.ClientTLS.(string); ok { - return strings.Join([]string{"", DEFAULT_PARTITION, as3SharedApplication, val}, "/"), true + return strings.Join([]string{"", val}, ""), true } log.Errorf("Unable to find serverssl for Data Group: %v\n", dgName) } @@ -609,9 +620,8 @@ func processCustomProfilesForAS3(customProfiles *CustomProfileStore, sharedApp a createCertificateDecl(prof, sharedApp) } else { createUpdateCABundle(prof, caBundleName, sharedApp) - if tlsClient == nil { - tlsClient = createTLSClient(prof, svcName, caBundleName, sharedApp) - } + tlsClient = createTLSClient(prof, svcName, caBundleName, sharedApp) + skey := SecretKey{ Name: prof.Name + "-ca", } @@ -692,7 +702,7 @@ func createTLSClient( sharedApp as3Application, ) *as3TLSClient { // For TLSClient only Cert (DestinationCACertificate) is given and key is empty string - if "" != prof.Cert && "" == prof.Key { + if _, ok := sharedApp[svcName]; "" != prof.Cert && "" == prof.Key && ok { svc := sharedApp[svcName].(*as3Service) tlsClientName := fmt.Sprintf("%s_tls_client", svcName) diff --git a/pkg/crmanager/crManager.go b/pkg/crmanager/crManager.go index d56358d6a..75e58de53 100644 --- a/pkg/crmanager/crManager.go +++ b/pkg/crmanager/crManager.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/crmanager/informers.go b/pkg/crmanager/informers.go index a140500a6..098ba9ac5 100644 --- a/pkg/crmanager/informers.go +++ b/pkg/crmanager/informers.go @@ -1,5 +1,5 @@ /*- -* Copyright (c) 2016-2019, F5 Networks, Inc. +* Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -143,7 +143,7 @@ func (crMgr *CRManager) addEventHandlers(crInf *CRInformer) { crInf.vsInformer.AddEventHandler( &cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { crMgr.enqueueVirtualServer(obj) }, - UpdateFunc: func(old, cur interface{}) { crMgr.enqueueVirtualServer(cur) }, + UpdateFunc: func(old, cur interface{}) { crMgr.enqueueUpdatedVirtualServer(old, cur) }, DeleteFunc: func(obj interface{}) { crMgr.enqueueDeletedVirtualServer(obj) }, }, ) @@ -202,6 +202,33 @@ func (crMgr *CRManager) enqueueVirtualServer(obj interface{}) { crMgr.rscQueue.Add(key) } +func (crMgr *CRManager) enqueueUpdatedVirtualServer(oldObj, newObj interface{}) { + oldVS := oldObj.(*cisapiv1.VirtualServer) + newVS := newObj.(*cisapiv1.VirtualServer) + + if oldVS.Spec.VirtualServerAddress != newVS.Spec.VirtualServerAddress { + log.Infof("Enqueueing VirtualServer: %v", oldVS) + key := &rqKey{ + namespace: oldVS.ObjectMeta.Namespace, + kind: VirtualServer, + rscName: oldVS.ObjectMeta.Name, + rsc: oldObj, + rscDelete: true, + } + crMgr.rscQueue.Add(key) + } + + log.Infof("Enqueueing VirtualServer: %v", newVS) + key := &rqKey{ + namespace: newVS.ObjectMeta.Namespace, + kind: VirtualServer, + rscName: newVS.ObjectMeta.Name, + rsc: newObj, + } + + crMgr.rscQueue.Add(key) +} + func (crMgr *CRManager) enqueueDeletedVirtualServer(obj interface{}) { vs := obj.(*cisapiv1.VirtualServer) log.Infof("Enqueueing VirtualServer: %v", vs) @@ -230,27 +257,46 @@ func (crMgr *CRManager) enqueueTLSServer(obj interface{}) { } func (crMgr *CRManager) enqueueService(obj interface{}) { + flag := true svc := obj.(*corev1.Service) - log.Infof("Enqueueing Service: %v", svc) - key := &rqKey{ - namespace: svc.ObjectMeta.Namespace, - kind: Service, - rscName: svc.ObjectMeta.Name, - rsc: obj, + log.Debugf("Enqueueing Service: %v", svc) + ignoresvcList := []string{"kube-dns", "kube-scheduler", "kube-controller-manager", "docker-registry", "kubernetes", "registry-console", "router", "kubelet", "console", "alertmanager-main", "alertmanager-operated", "cluster-monitoring-operator", "grafana", "kube-state-metrics", "node-exporter", "prometheus-k8s", "prometheus-operated", "prometheus-operatorwebconsole"} + for _, svcName := range ignoresvcList { + if svc.ObjectMeta.Name == svcName { + flag = false + break + } + } + if flag { + key := &rqKey{ + namespace: svc.ObjectMeta.Namespace, + kind: Service, + rscName: svc.ObjectMeta.Name, + rsc: obj, + } + crMgr.rscQueue.Add(key) } - - crMgr.rscQueue.Add(key) } func (crMgr *CRManager) enqueueEndpoints(obj interface{}) { + flag := true eps := obj.(*corev1.Endpoints) - log.Infof("Enqueueing Endpoints: %v", eps) - key := &rqKey{ - namespace: eps.ObjectMeta.Namespace, - kind: Endpoints, - rscName: eps.ObjectMeta.Name, - rsc: obj, + log.Debugf("Enqueueing Endpoints: %v", eps) + ignoreeplist := []string{"kube-dns", "kube-scheduler", "kube-controller-manager", "docker-registry", "kubernetes", "registry-console", "router", "kubelet", "console", "alertmanager-main", "alertmanager-operated", "cluster-monitoring-operator", "grafana", "kube-state-metrics", "node-exporter", "prometheus-k8s", "prometheus-operated", "prometheus-operatorwebconsole"} + for _, epname := range ignoreeplist { + if eps.ObjectMeta.Name == epname { + flag = false + break + } } + if flag { + key := &rqKey{ + namespace: eps.ObjectMeta.Namespace, + kind: Endpoints, + rscName: eps.ObjectMeta.Name, + rsc: obj, + } - crMgr.rscQueue.Add(key) + crMgr.rscQueue.Add(key) + } } diff --git a/pkg/crmanager/postManager.go b/pkg/crmanager/postManager.go index d25be02ba..f506b63de 100644 --- a/pkg/crmanager/postManager.go +++ b/pkg/crmanager/postManager.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/crmanager/profile.go b/pkg/crmanager/profile.go index 0511c3f47..422d50de1 100644 --- a/pkg/crmanager/profile.go +++ b/pkg/crmanager/profile.go @@ -2,23 +2,94 @@ package crmanager import ( "fmt" - v1 "k8s.io/api/core/v1" "reflect" + + v1 "k8s.io/api/core/v1" ) -// Creates a default SNI profile (if needed) and a new profile from a Secret -func (crMgr *CRManager) createSecretSslProfile( +// Creates a new ClientSSL profile from a Secret +func (crMgr *CRManager) createSecretClientSSLProfile( rsCfg *ResourceConfig, secret *v1.Secret, context string, ) (error, bool) { + + if _, ok := secret.Data["tls.key"]; !ok { + err := fmt.Errorf("Invalid Secret '%v': 'tls.key' field not specified.", + secret.ObjectMeta.Name) + return err, false + } + if _, ok := secret.Data["tls.crt"]; !ok { err := fmt.Errorf("Invalid Secret '%v': 'tls.crt' field not specified.", secret.ObjectMeta.Name) return err, false } - if _, ok := secret.Data["tls.key"]; !ok { - err := fmt.Errorf("Invalid Secret '%v': 'tls.key' field not specified.", + + // Create Default for SNI profile + skey := SecretKey{ + Name: fmt.Sprintf("default-%s-%s", context, rsCfg.GetName()), + ResourceName: rsCfg.GetName(), + } + sni := ProfileRef{ + Name: skey.Name, + Partition: rsCfg.Virtual.Partition, + Context: context, + } + if _, ok := crMgr.customProfiles.Profs[skey]; !ok { + // This is just a basic profile, so we don't need all the fields + cp := NewCustomProfile(sni, "", "", "", true, "", "") + crMgr.customProfiles.Profs[skey] = cp + } + // TODO + //rsCfg.Virtual.AddOrUpdateProfile(sni) + + // Now add the resource profile + profRef := ProfileRef{ + Name: secret.ObjectMeta.Name, + Partition: rsCfg.Virtual.Partition, + Context: context, + Namespace: secret.ObjectMeta.Namespace, + } + cp := NewCustomProfile( + profRef, + string(secret.Data["tls.crt"]), + string(secret.Data["tls.key"]), + "", // serverName + false, // sni + "", // peerCertMode + "", // caFile + ) + skey = SecretKey{ + Name: cp.Name, + ResourceName: rsCfg.GetName(), + } + crMgr.customProfiles.Lock() + defer crMgr.customProfiles.Unlock() + if prof, ok := crMgr.customProfiles.Profs[skey]; ok { + if !reflect.DeepEqual(prof, cp) { + crMgr.customProfiles.Profs[skey] = cp + rsCfg.Virtual.AddOrUpdateProfile(profRef) + return nil, true + } else { + return nil, false + } + } + crMgr.customProfiles.Profs[skey] = cp + rsCfg.Virtual.AddOrUpdateProfile(profRef) + return nil, false +} + +// Creates a new ServerSSL profile from a Secret +func (crMgr *CRManager) createSecretServerSSLProfile( + rsCfg *ResourceConfig, + secret *v1.Secret, + context string, +) (error, bool) { + + // tls.key is not mandatory for ServerSSL Profile + if _, ok := secret.Data["tls.crt"]; !ok { + err := fmt.Errorf("Invalid Secret '%v': 'tls.crt' field not specified.", secret.ObjectMeta.Name) return err, false } @@ -51,7 +122,7 @@ func (crMgr *CRManager) createSecretSslProfile( cp := NewCustomProfile( profRef, string(secret.Data["tls.crt"]), - string(secret.Data["tls.key"]), + "", "", // serverName false, // sni "", // peerCertMode diff --git a/pkg/crmanager/pythonDriver.go b/pkg/crmanager/pythonDriver.go index 5bb225a4d..1037929d4 100644 --- a/pkg/crmanager/pythonDriver.go +++ b/pkg/crmanager/pythonDriver.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2019, F5 Networks, Inc. + * Copyright (c) 2019-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/crmanager/resourceConfig.go b/pkg/crmanager/resourceConfig.go index 72ab61c46..d24f61a94 100644 --- a/pkg/crmanager/resourceConfig.go +++ b/pkg/crmanager/resourceConfig.go @@ -1,5 +1,5 @@ /*- -* Copyright (c) 2016-2019, F5 Networks, Inc. +* Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -318,6 +318,8 @@ func formatVirtualServerName(ip string, port int32) string { func formatVirtualServerPoolName(namespace, svc string, nodeMemberLabel string) string { poolName := fmt.Sprintf("%s_%s", namespace, svc) if nodeMemberLabel != "" { + replacer := strings.NewReplacer("=", "_") + nodeMemberLabel = replacer.Replace(nodeMemberLabel) poolName = fmt.Sprintf("%s_%s", poolName, nodeMemberLabel) } return AS3NameFormatter(poolName) @@ -341,7 +343,7 @@ func (crMgr *CRManager) prepareRSConfigFromVirtualServer( var pools Pools var rules *Rules var plcy *Policy - + var poolExist bool var monitors []Monitor for _, pl := range vs.Spec.Pools { pool := Pool{ @@ -355,6 +357,16 @@ func (crMgr *CRManager) prepareRSConfigFromVirtualServer( ServicePort: pl.ServicePort, NodeMemberLabel: pl.NodeMemberLabel, } + for _, p := range pools { + if pool.Name == p.Name { + poolExist = true + break + } + } + if poolExist { + poolExist = false + continue + } if pl.Monitor.Send != "" && pl.Monitor.Type != "" { pool.MonitorNames = append(pool.MonitorNames, JoinBigipPath(DEFAULT_PARTITION, @@ -459,7 +471,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( if secret, ok := crMgr.SSLContext[clientSSL]; ok { log.Debugf("clientSSL secret %s for TLSProfile '%s' is already available with CIS in "+ "SSLContext as clientSSL", secret.ObjectMeta.Name, tlsName) - err, _ := crMgr.createSecretSslProfile(rsCfg, secret, CustomProfileClient) + err, _ := crMgr.createSecretClientSSLProfile(rsCfg, secret, CustomProfileClient) if err != nil { log.Debugf("error %v encountered for '%s' using TLSProfile '%s'", err, vsName, tlsName) @@ -468,8 +480,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( } else { // Check if profile is contained in a Secret // Update the SSL Context if secret found, This is used to avoid api calls - log.Debugf("clientSSL secret for TLSProfile '%s' does not exist with CIS in "+ - "SSLContext", tlsName) + log.Debugf("saving clientSSL secret for TLSProfile '%s' into SSLContext", tlsName) secret, err := crMgr.kubeClient.CoreV1().Secrets(vsNamespace). Get(clientSSL, metav1.GetOptions{}) if err != nil { @@ -478,7 +489,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( return false } crMgr.SSLContext[clientSSL] = secret - error, _ := crMgr.createSecretSslProfile(rsCfg, secret, CustomProfileClient) + error, _ := crMgr.createSecretClientSSLProfile(rsCfg, secret, CustomProfileClient) if error != nil { log.Errorf("error %v encountered for '%s' using TLSProfile '%s'", error, vsName, tlsName) @@ -492,7 +503,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( if secret, ok := crMgr.SSLContext[serverSSL]; ok { log.Debugf("serverSSL secret %s for TLSProfile '%s' is already available with CIS in"+ "SSLContext", secret.ObjectMeta.Name, tlsName) - err, _ := crMgr.createSecretSslProfile(rsCfg, secret, CustomProfileServer) + err, _ := crMgr.createSecretServerSSLProfile(rsCfg, secret, CustomProfileServer) if err != nil { log.Debugf("error %v encountered for '%s' using TLSProfile '%s'", err, vsName, tlsName) @@ -501,8 +512,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( } else { // Check if profile is contained in a Secret // Update the SSL Context if secret found, This is used to avoid api calls - log.Debugf("serverSSL secret for TLSProfile '%s' does not exist with CIS in "+ - "SSLContext", tlsName) + log.Debugf("saving serverSSL secret for TLSProfile '%s' into SSLContext", tlsName) secret, err := crMgr.kubeClient.CoreV1().Secrets(vsNamespace). Get(serverSSL, metav1.GetOptions{}) if err != nil { @@ -511,7 +521,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( return false } crMgr.SSLContext[serverSSL] = secret - error, _ := crMgr.createSecretSslProfile(rsCfg, secret, CustomProfileServer) + error, _ := crMgr.createSecretServerSSLProfile(rsCfg, secret, CustomProfileServer) if error != nil { log.Errorf("error %v encountered for '%s' using TLSProfile '%s'", error, vsName, tlsName) @@ -526,8 +536,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( } // TLS Cert/Key for _, pl := range vs.Spec.Pools { - if "" != vs.Spec.TLSProfileName && - pl.ServicePort == DEFAULT_HTTPS_PORT { + if "" != vs.Spec.TLSProfileName { switch tls.Spec.TLS.Termination { case TLSEdge: serverSsl := "false" @@ -543,7 +552,7 @@ func (crMgr *CRManager) handleVirtualServerTLS( path := pl.Path sslPath := hostName + path sslPath = strings.TrimSuffix(sslPath, "/") - serverSsl := AS3NameFormatter("f5_crd_" + vs.Spec.VirtualServerAddress + "_tls_client") + serverSsl := AS3NameFormatter("crd_" + vs.Spec.VirtualServerAddress + "_tls_client") if "" != tls.Spec.TLS.ServerSSL { updateDataGroup(crMgr.intDgMap, ReencryptServerSslDgName, DEFAULT_PARTITION, vs.ObjectMeta.Namespace, sslPath, serverSsl) @@ -561,6 +570,11 @@ func (crMgr *CRManager) handleVirtualServerTLS( PassthroughHostsDgName, ) case TLSReencrypt: + if vs.Spec.HTTPTraffic == TLSAllowInsecure { + log.Errorf("Error in processing Virtual '%s' using TLSProfile '%s' as httpTraffic is configured as ALLOW for reencrypt Termination", + vsName, tlsName) + return false + } updateDataGroupOfDgName( crMgr.intDgMap, vs, @@ -621,6 +635,40 @@ func (crMgr *CRManager) handleVirtualServerTLS( return true } +// validate TLSProfile +// validation includes valid parameters for the type of termination(edge, re-encrypt and Pass-through) +func validateTLSProfile(tls *cisapiv1.TLSProfile) bool { + //validation for re-encrypt termination + if tls.Spec.TLS.Termination == "reencrypt" { + // Should contain both client and server SSL profiles + if (tls.Spec.TLS.ClientSSL == "") || (tls.Spec.TLS.ServerSSL == "") { + log.Errorf("TLSProfile %s of type re-encrypt termination should contain both "+ + "ClientSSL and ServerSSL", tls.ObjectMeta.Name) + return false + } + } else if tls.Spec.TLS.Termination == "edge" { + // Should contain only client SSL + if tls.Spec.TLS.ClientSSL == "" { + log.Errorf("TLSProfile %s of type edge termination should contain Client SSL", + tls.ObjectMeta.Name) + return false + } + if tls.Spec.TLS.ServerSSL != "" { + log.Errorf("TLSProfile %s of type edge termination should NOT contain ServerSSL", + tls.ObjectMeta.Name) + return false + } + } else { + // Pass-through + if (tls.Spec.TLS.ClientSSL != "") || (tls.Spec.TLS.ServerSSL != "") { + log.Errorf("TLSProfile %s of type Pass-through termination should NOT contain either "+ + "ClientSSL or ServerSSL", tls.ObjectMeta.Name) + return false + } + } + return true +} + // ConvertStringToProfileRef converts strings to profile references func ConvertStringToProfileRef(profileName, context, ns string) ProfileRef { profName := strings.TrimSpace(strings.TrimPrefix(profileName, "/")) diff --git a/pkg/crmanager/routing.go b/pkg/crmanager/routing.go index fd98dbc9c..2b270f790 100644 --- a/pkg/crmanager/routing.go +++ b/pkg/crmanager/routing.go @@ -1,5 +1,5 @@ /*- -* Copyright (c) 2016-2019, F5 Networks, Inc. +* Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -757,13 +757,13 @@ func updateDataGroupOfDgName( path := pl.Path routePath := hostName + path routePath = strings.TrimSuffix(routePath, "/") - poolName := formatVirtualServerPoolName(namespace, pl.Service, "") + poolName := formatVirtualServerPoolName(namespace, pl.Service, pl.NodeMemberLabel) updateDataGroup(intDgMap, dgName, DEFAULT_PARTITION, namespace, routePath, poolName) } case PassthroughHostsDgName: for _, pl := range virtual.Spec.Pools { - poolName := formatVirtualServerPoolName(namespace, pl.Service, "") + poolName := formatVirtualServerPoolName(namespace, pl.Service, pl.NodeMemberLabel) updateDataGroup(intDgMap, dgName, DEFAULT_PARTITION, namespace, hostName, poolName) } diff --git a/pkg/crmanager/types.go b/pkg/crmanager/types.go index 2eaaa8de5..4302f6641 100644 --- a/pkg/crmanager/types.go +++ b/pkg/crmanager/types.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/crmanager/validate.go b/pkg/crmanager/validate.go index 7ee06c718..68b5eb60b 100644 --- a/pkg/crmanager/validate.go +++ b/pkg/crmanager/validate.go @@ -1,5 +1,5 @@ /*- -* Copyright (c) 2016-2019, F5 Networks, Inc. +* Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/crmanager/worker.go b/pkg/crmanager/worker.go index 0806db6c5..05b67e063 100644 --- a/pkg/crmanager/worker.go +++ b/pkg/crmanager/worker.go @@ -1,5 +1,5 @@ /*- -* Copyright (c) 2016-2019, F5 Networks, Inc. +* Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -186,7 +186,7 @@ func (crMgr *CRManager) syncService(svc *v1.Service) []*cisapiv1.VirtualServer { // find VirtualServers that reference the service virtualsForService := getVirtualServersForService(allVirtuals, svc) if nil == virtualsForService { - log.Infof("Change in Service %s does not effect any VirtualServer", + log.Debugf("Change in Service %s does not effect any VirtualServer", svc.ObjectMeta.Name) return nil } @@ -307,7 +307,17 @@ func getVirtualServersForTLSProfile(allVirtuals []*cisapiv1.VirtualServer, for _, vs := range allVirtuals { if vs.ObjectMeta.Namespace == tlsNamespace && vs.Spec.TLSProfileName == tlsName { - result = append(result, vs) + found := false + for _, host := range tls.Spec.Hosts { + if vs.Spec.Host == host { + result = append(result, vs) + found = true + break + } + } + if !found { + log.Errorf("TLSProfile hostname is not same as virtual host %s for profile %s", vs.Spec.Host, vs.Spec.TLSProfileName) + } } } @@ -334,8 +344,22 @@ func (crMgr *CRManager) getTLSProfileForVirtualServer(vs *cisapiv1.VirtualServer return nil } - // TLSProfile Object - return obj.(*cisapiv1.TLSProfile) + // validate TLSProfile + validation := validateTLSProfile(obj.(*cisapiv1.TLSProfile)) + if validation == false { + return nil + } + + tlsProfile := obj.(*cisapiv1.TLSProfile) + for _, host := range tlsProfile.Spec.Hosts { + if host == vs.Spec.Host { + // TLSProfile Object + return tlsProfile + } + } + log.Errorf("TLSProfile %s with host %s does not match with virtual server %s host.", tlsName, vs.Spec.Host, vs.ObjectMeta.Name) + return nil + } // syncVirtualServers takes the Virtual Server as input and processes all @@ -371,6 +395,7 @@ func (crMgr *CRManager) syncVirtualServers( // Prepare list of associated VirtualServers to be processed // In the event of deletion, exclude the deleted VirtualServer + log.Debugf("Process all the Virtual Servers which share same VirtualServerAddress") for _, v := range allVirtuals { if v.Spec.VirtualServerAddress == virtual.Spec.VirtualServerAddress && v.Spec.Host == virtual.Spec.Host && @@ -410,14 +435,16 @@ func (crMgr *CRManager) syncVirtualServers( ) for _, vrt := range virtuals { + log.Debugf("Processing Virtual Server %s for port %v", + vrt.ObjectMeta.Name, portStruct.port) crMgr.prepareRSConfigFromVirtualServer( rsCfg, vrt, ) - if len(virtual.Spec.TLSProfileName) != 0 { + if len(vrt.Spec.TLSProfileName) != 0 { // Handle TLS configuration for VirtualServer Custom Resource - processed := crMgr.handleVirtualServerTLS(rsCfg, virtual) + processed := crMgr.handleVirtualServerTLS(rsCfg, vrt) if !processed { // Processing failed // Stop processing further virtuals @@ -426,18 +453,15 @@ func (crMgr *CRManager) syncVirtualServers( } log.Debugf("Updated Virtual %s with TLSProfile %s", - virtual.ObjectMeta.Name, virtual.Spec.TLSProfileName) + vrt.ObjectMeta.Name, vrt.Spec.TLSProfileName) } } if processingError { - log.Errorf("Cannot Publish VirtualServer %s with invalid/non-existing TLSProfile %s", - virtual.ObjectMeta.Name, virtual.Spec.TLSProfileName) + log.Errorf("Cannot Publish VirtualServer %s", virtual.ObjectMeta.Name) break } - log.Debugf("ResourceConfig looks like %v", rsCfg) - // Save ResourceConfig in temporary Map vsMap[rsName] = rsCfg diff --git a/pkg/pollers/nodePoller.go b/pkg/pollers/nodePoller.go index 35f142f8d..098d7f6f0 100644 --- a/pkg/pollers/nodePoller.go +++ b/pkg/pollers/nodePoller.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import ( log "github.com/F5Networks/k8s-bigip-ctlr/pkg/vlogger" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" diff --git a/pkg/pollers/nodePoller_test.go b/pkg/pollers/nodePoller_test.go index 788ca7fe6..09b890af5 100644 --- a/pkg/pollers/nodePoller_test.go +++ b/pkg/pollers/nodePoller_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" ) diff --git a/pkg/pollers/pollers.go b/pkg/pollers/pollers.go index 5ed3dde22..e729cea37 100644 --- a/pkg/pollers/pollers.go +++ b/pkg/pollers/pollers.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/resource/resourceConfig.go b/pkg/resource/resourceConfig.go index 94793f2d4..ec3a904db 100644 --- a/pkg/resource/resourceConfig.go +++ b/pkg/resource/resourceConfig.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/resource/resourceConfig_test.go b/pkg/resource/resourceConfig_test.go index 64e1d1eda..20607099c 100644 --- a/pkg/resource/resourceConfig_test.go +++ b/pkg/resource/resourceConfig_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/resource/routeDomain.go b/pkg/resource/routeDomain.go index 29897c981..fd8b30d01 100644 --- a/pkg/resource/routeDomain.go +++ b/pkg/resource/routeDomain.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/resource/types.go b/pkg/resource/types.go index 4121f3335..dc60403ed 100644 --- a/pkg/resource/types.go +++ b/pkg/resource/types.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2019, F5 Networks, Inc. + * Copyright (c) 2016-2020, F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/test/configs/as3_route_cfgmap_declaration.json b/pkg/test/configs/as3_route_cfgmap_declaration.json index eac3b37ca..0106f9694 100644 --- a/pkg/test/configs/as3_route_cfgmap_declaration.json +++ b/pkg/test/configs/as3_route_cfgmap_declaration.json @@ -1,9 +1,9 @@ { - "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.20.0/as3-schema-3.20.0-3.json", + "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.21.0/as3-schema-3.21.0-4.json", "class": "AS3", "declaration": { "class": "ADC", - "schemaVersion": "3.20.0", + "schemaVersion": "3.21.0", "id": "urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d", "label": "CIS Declaration", "remark": "Auto-generated by CIS", diff --git a/pkg/test/configs/as3_route_declaration.json b/pkg/test/configs/as3_route_declaration.json index 1ab21cc3b..d0709152a 100644 --- a/pkg/test/configs/as3_route_declaration.json +++ b/pkg/test/configs/as3_route_declaration.json @@ -1,9 +1,9 @@ { - "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.20.0/as3-schema-3.20.0-3.json", + "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.21.0/as3-schema-3.21.0-4.json", "class": "AS3", "declaration": { "class": "ADC", - "schemaVersion": "3.20.0", + "schemaVersion": "3.21.0", "id": "urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d", "label": "CIS Declaration", "remark": "Auto-generated by CIS", diff --git a/pkg/test/configs/as3_route_declaration_overridden.json b/pkg/test/configs/as3_route_declaration_overridden.json index 4e68ccb25..373f709eb 100644 --- a/pkg/test/configs/as3_route_declaration_overridden.json +++ b/pkg/test/configs/as3_route_declaration_overridden.json @@ -1,9 +1,9 @@ { - "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.20.0/as3-schema-3.20.0-3.json", + "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.21.0/as3-schema-3.21.0-4.json", "class": "AS3", "declaration": { "class": "ADC", - "schemaVersion": "3.20.0", + "schemaVersion": "3.21.0", "id": "urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d", "label": "CIS Declaration", "remark": "Auto-generated by CIS", diff --git a/pkg/test/configs/as3config_multi_cm_unified.json b/pkg/test/configs/as3config_multi_cm_unified.json index 0bbca9aa6..1e2952c3f 100644 --- a/pkg/test/configs/as3config_multi_cm_unified.json +++ b/pkg/test/configs/as3config_multi_cm_unified.json @@ -1,9 +1,9 @@ { - "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.20.0/as3-schema-3.20.0-3.json", + "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/3.21.0/as3-schema-3.21.0-4.json", "class": "AS3", "declaration": { "class": "ADC", - "schemaVersion": "3.20.0", + "schemaVersion": "3.21.0", "id": "urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d", "label": "CIS Declaration", "remark": "Auto-generated by CIS", diff --git a/pkg/test/utils.go b/pkg/test/utils.go index 39a7ea3c7..e91e56197 100644 --- a/pkg/test/utils.go +++ b/pkg/test/utils.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import ( "github.com/F5Networks/k8s-bigip-ctlr/pkg/pollers" routeapi "github.com/openshift/api/route/v1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/vlogger/console/log_console.go b/pkg/vlogger/console/log_console.go index 8d7d85734..2c0c275fa 100644 --- a/pkg/vlogger/console/log_console.go +++ b/pkg/vlogger/console/log_console.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019, F5 Networks, Inc. +// Copyright (c) 2019-2020, F5 Networks, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/vlogger/doc.go b/pkg/vlogger/doc.go index f61337d67..63b914f78 100644 --- a/pkg/vlogger/doc.go +++ b/pkg/vlogger/doc.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019, F5 Networks, Inc. +// Copyright (c) 2019-2020, F5 Networks, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/vlogger/log.go b/pkg/vlogger/log.go index b70addce3..b070d8f40 100644 --- a/pkg/vlogger/log.go +++ b/pkg/vlogger/log.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019, F5 Networks, Inc. +// Copyright (c) 2019-2020, F5 Networks, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/vlogger/log_null.go b/pkg/vlogger/log_null.go index b66c9e38d..6265e7fe0 100644 --- a/pkg/vlogger/log_null.go +++ b/pkg/vlogger/log_null.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019, F5 Networks, Inc. +// Copyright (c) 2019-2020, F5 Networks, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/vxlan/vxlanMgr.go b/pkg/vxlan/vxlanMgr.go index f48b95b13..57a3c3196 100644 --- a/pkg/vxlan/vxlanMgr.go +++ b/pkg/vxlan/vxlanMgr.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/vxlan/vxlanMgr_test.go b/pkg/vxlan/vxlanMgr_test.go index 111fa7c68..479ba3fd3 100644 --- a/pkg/vxlan/vxlanMgr_test.go +++ b/pkg/vxlan/vxlanMgr_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/writer/configWriter.go b/pkg/writer/configWriter.go index 7a78a9c95..aa5740a76 100644 --- a/pkg/writer/configWriter.go +++ b/pkg/writer/configWriter.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pkg/writer/configWriter_test.go b/pkg/writer/configWriter_test.go index 0a39e566a..b4e0833c1 100644 --- a/pkg/writer/configWriter_test.go +++ b/pkg/writer/configWriter_test.go @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2017,2018,2019 F5 Networks, Inc. + * Copyright (c) 2017-2020 F5 Networks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/schemas/as3-schema-3.20.0-3-cis.json b/schemas/as3-schema-3.21.0-4-cis.json similarity index 99% rename from schemas/as3-schema-3.20.0-3-cis.json rename to schemas/as3-schema-3.21.0-4-cis.json index bba0d4be9..2ac706bd1 100644 --- a/schemas/as3-schema-3.20.0-3-cis.json +++ b/schemas/as3-schema-3.21.0-4-cis.json @@ -996,6 +996,14 @@ "type": "boolean", "default": false }, + "cacheTimeout": { + "title": "Cache Timeout", + "description": "Sets the cache timeout (in seconds)", + "type": "integer", + "maximum": 86400, + "minimum": 0, + "default": 3600 + }, "certificates": { "title": "Certificates", "description": "Primary and (optional) additional certificates (order is significant, element 0 is primary cert)", @@ -3258,6 +3266,14 @@ "format": "hostname", "default": "none" }, + "cacheTimeout": { + "title": "Cache Timeout", + "description": "Sets the cache timeout (in seconds)", + "type": "integer", + "maximum": 86400, + "minimum": 0, + "default": 3600 + }, "ciphers": { "title": "Ciphers", "description": "Ciphersuite selection string. ciphers and cipherGroup are mutually exclusive, only use one.", @@ -9527,6 +9543,17 @@ "minItems": 1, "uniqueItems": true }, + "serviceDownImmediateAction": { + "title": "Service Down Immediate Action", + "description": "Specifies the immediate action the BIG-IP system should respond with upon the receipt of the initial client's SYN packet if the availability status of the virtual server is Offline or Unavailable. This is supported for the virtual server of Standard type and TCP protocol. The default value is none.", + "type": "string", + "enum": [ + "none", + "drop", + "reset" + ], + "default": "none" + }, "shareAddresses": { "title": "Share addresses", "description": "A user set boolean that indicates whether the virtualAddresses should be added to or checked for /Common instead of the tenant. This value defaults to false, and so will put the virtualAddresses into their tenant.", @@ -12981,9 +13008,6 @@ "type": "object", "additionalProperties": { "$ref": "#/definitions/DNS_Zone_Local" - }, - "propertyNames": { - "format": "hostname" } }, "messageCacheSize": { @@ -16644,7 +16668,7 @@ "description": "Maximum number of response octets to buffer before deciding whether to apply compression (default 4096)", "type": "integer", "minimum": 256, - "maximum": 32768, + "maximum": 4294967295, "default": 4096 }, "contentTypeExcludes": { @@ -17885,6 +17909,11 @@ "items": { "$ref": "#/definitions/Pointer_GSLB_Monitor" } + }, + "name": { + "title": "Name", + "description": "Specifies the name of the Virtual Server", + "type": "string" } }, "required": [ @@ -27796,6 +27825,7 @@ "type": "string", "$comment": "IMPORTANT: In enum array, please put current schema version first, oldest-supported version last. Keep enum array sorted most-recent-first.", "enum": [ + "3.21.0", "3.20.0", "3.19.0", "3.18.0",