diff --git a/common-controller/go.mod b/common-controller/go.mod index 3841e339f..63205973a 100644 --- a/common-controller/go.mod +++ b/common-controller/go.mod @@ -15,6 +15,7 @@ require ( require ( github.com/envoyproxy/go-control-plane v0.11.2-0.20230802074621-eea0b3bd0f81 github.com/gin-gonic/gin v1.9.1 + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/pelletier/go-toml v1.9.5 github.com/redis/go-redis/v9 v9.2.1 github.com/wso2/apk/adapter v0.0.0-20231207051518-6dd728943082 diff --git a/common-controller/go.sum b/common-controller/go.sum index caa9595a8..7c9354421 100644 --- a/common-controller/go.sum +++ b/common-controller/go.sum @@ -29,6 +29,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -44,6 +45,7 @@ github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhF github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.11.2-0.20230802074621-eea0b3bd0f81 h1:dx15VeDt3L5Z0Wx28jXbwgpeTrLsVvqC/wSvNgYPb/k= github.com/envoyproxy/go-control-plane v0.11.2-0.20230802074621-eea0b3bd0f81/go.mod h1:zV+ml0OfGpQxGvM1qlmhvZzE9ShvBO7CPWzGb3q5cog= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -60,6 +62,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -78,6 +82,7 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -91,6 +96,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -117,6 +123,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -130,6 +138,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -157,6 +167,7 @@ github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= @@ -180,15 +191,19 @@ github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -208,12 +223,15 @@ github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -260,6 +278,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -305,6 +324,7 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230731193218-e0aa005b6bdf h1:v5Cf4E9+6tawYrs/grq1q1hFpGtzlGFzgWHqwt6NFiU= @@ -317,6 +337,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/common-controller/internal/config/default_config.go b/common-controller/internal/config/default_config.go index ce4456b00..081122c36 100644 --- a/common-controller/internal/config/default_config.go +++ b/common-controller/internal/config/default_config.go @@ -35,5 +35,12 @@ var defaultConfig = &Config{ }, Environment: "Default", InternalAPIServer: internalAPIServer{Port: 18003}, + ControlPlane: controlplane{ + Enabled: false, + Host: "localhost", + EventPort: 18000, + RestPort: 18001, + RetryInterval: 5, + Persistence: persistence{Type: "K8s"}}, }, } diff --git a/common-controller/internal/config/parser.go b/common-controller/internal/config/parser.go index 98a5af169..3b661760f 100644 --- a/common-controller/internal/config/parser.go +++ b/common-controller/internal/config/parser.go @@ -18,9 +18,12 @@ package config import ( - "io/ioutil" + "fmt" "os" "reflect" + "regexp" + "strconv" + "strings" "sync" toml "github.com/pelletier/go-toml" @@ -30,8 +33,6 @@ import ( var ( onceConfigRead sync.Once - apkHome string - logConfigPath string controllerConfig *Config envVariableMap map[string]string ) @@ -39,6 +40,11 @@ var ( const ( // RelativeConfigPath is the relative file path where the configuration file is. relativeConfigPath = "/conf/config.toml" + // EnvConfigPrefix is used when configs should be read from environment variables. + EnvConfigPrefix = "$env" + envVariablePrefix = "APK_" + // envVariableEntrySeparator is used as the separator used to denote nested structured properties. + envVariableEntrySeparator = "_" ) // ReadConfigs implements adapter configuration read operation. The read operation will happen only once, hence @@ -57,7 +63,7 @@ func ReadConfigs() *Config { if err != nil { loggerConfig.ErrorC(logging.PrintError(logging.Error1000, logging.BLOCKER, "Configuration file not found, error: %v", err.Error())) } - content, readErr := ioutil.ReadFile(pkgconf.GetApkHome() + relativeConfigPath) + content, readErr := os.ReadFile(pkgconf.GetApkHome() + relativeConfigPath) if readErr != nil { loggerConfig.ErrorC(logging.PrintError(logging.Error1001, logging.BLOCKER, "Error reading configurations, error: %v", readErr.Error())) return @@ -83,3 +89,278 @@ func ClearLogConfigInstance() { func GetLogConfigPath() (string, error) { return pkgconf.GetLogConfigPath() } + +// ResolveConfigEnvValues looks for the string type config values which should be read from environment variables +// and replace the respective config values from environment variable. +// v - relect.Value of the root level struct +// previousTag - the starting Tag corresponding to the root level struct +// resolveEnvTag - true if $env{} annotation needs to be resolved at adapter level +func ResolveConfigEnvValues(v reflect.Value, previousTag string, resolveEnvTag bool) { + s := v + for fieldNum := 0; fieldNum < s.NumField(); fieldNum++ { + field := s.Field(fieldNum) + currentTag := previousTag + envVariableEntrySeparator + s.Type().Field(fieldNum).Name + resolveEnvForReflectValue(field, currentTag, resolveEnvTag) + } +} + +func resolveEnvForReflectValue(field reflect.Value, currentTag string, resolveEnvTag bool) { + fieldKind := getKind(field) + + switch fieldKind { + case reflect.String: + if strings.Contains(fmt.Sprint(field.Interface()), EnvConfigPrefix) && resolveEnvTag { + field.SetString(ResolveEnvValue(fmt.Sprint(field.Interface()))) + } + resolveEnvStringValue(currentTag, field) + case reflect.Slice: + resolveEnvValueOfArray(field, currentTag, resolveEnvTag) + case reflect.Array: + // this condition is never reached. + resolveEnvValueOfArray(field, currentTag, resolveEnvTag) + case reflect.Struct: + if field.Kind() == reflect.Struct { + ResolveConfigEnvValues(field.Addr().Elem(), currentTag, resolveEnvTag) + } + case reflect.Bool: + resolveEnvBooleanValue(currentTag, field) + case reflect.Int: + resolveEnvIntValue(currentTag, field) + case reflect.Float32: + resolveEnvFloat32Value(currentTag, field) + case reflect.Uint: + resolveEnvUIntValue(currentTag, field) + case reflect.Map: + resolveEnvValueOfMap(field, currentTag, resolveEnvTag) + // this condition is never reached.: + case reflect.Ptr: + } +} + +func getKind(val reflect.Value) reflect.Kind { + kind := val.Kind() + + switch { + case kind >= reflect.Int && kind <= reflect.Int64: + return reflect.Int + case kind >= reflect.Uint && kind <= reflect.Uint64: + return reflect.Uint + case kind >= reflect.Float32 && kind <= reflect.Float64: + return reflect.Float32 + default: + return kind + } +} + +func resolveEnvValueOfArray(field reflect.Value, currentTag string, resolveEnvTag bool) { + var arrayElementType reflect.Kind + for index := 0; index < field.Len(); index++ { + arrayElementType = field.Index(index).Kind() + if field.Index(index).Kind() == reflect.Struct { + ResolveConfigEnvValues(field.Index(index).Addr().Elem(), currentTag+envVariableEntrySeparator+strconv.Itoa(index), resolveEnvTag) + } else if field.Index(index).Kind() == reflect.String && strings.Contains(field.Index(index).String(), + EnvConfigPrefix) && resolveEnvTag { + field.Index(index).SetString(ResolveEnvValue(field.Index(index).String())) + } + } + + if arrayElementType == reflect.Invalid { + indirectStr := reflect.Indirect(field) + valueSlice := reflect.MakeSlice(indirectStr.Type(), 1, 1) + arrayElementType = valueSlice.Index(0).Kind() + } + + variableValue, exists := envVariableMap[strings.ToUpper(envVariablePrefix+currentTag)] + if exists { + elementArrayAsString := splitStringAndTrim(variableValue) + switch arrayElementType { + case reflect.String: + field.Set(reflect.ValueOf(elementArrayAsString)) + case reflect.Int: + elementArrayAsInt := make([]int, len(elementArrayAsString)) + var parseErr error + for index, stringElem := range elementArrayAsString { + elementArrayAsInt[index], parseErr = strconv.Atoi(stringElem) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an integer array", variableValue) + return + } + } + field.Set(reflect.ValueOf(elementArrayAsInt)) + case reflect.Int32: + elementArrayAsInt := make([]int32, len(elementArrayAsString)) + for index, stringElem := range elementArrayAsString { + int64Val, parseErr := strconv.ParseInt(stringElem, 10, 32) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an integer32 array", variableValue) + return + } + elementArrayAsInt[index] = int32(int64Val) + } + field.Set(reflect.ValueOf(elementArrayAsInt)) + case reflect.Int64: + elementArrayAsInt := make([]int64, len(elementArrayAsString)) + for index, stringElem := range elementArrayAsString { + int64Val, parseErr := strconv.ParseInt(stringElem, 10, 64) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an integer64 array", variableValue) + return + } + elementArrayAsInt[index] = int64Val + } + field.Set(reflect.ValueOf(elementArrayAsInt)) + case reflect.Uint: + elementArrayAsUInt := make([]uint, len(elementArrayAsString)) + for index, stringElem := range elementArrayAsString { + uint64val, parseErr := strconv.ParseUint(stringElem, 10, 32) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an unsigned integer array", variableValue) + return + } + elementArrayAsUInt[index] = uint(uint64val) + } + field.Set(reflect.ValueOf(elementArrayAsUInt)) + case reflect.Uint32: + elementArrayAsUInt := make([]uint32, len(elementArrayAsString)) + for index, stringElem := range elementArrayAsString { + uint64Val, parseErr := strconv.ParseUint(stringElem, 10, 32) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an unsigned integer32 array", variableValue) + return + } + elementArrayAsUInt[index] = uint32(uint64Val) + } + field.Set(reflect.ValueOf(elementArrayAsUInt)) + case reflect.Uint64: + elementArrayAsInt := make([]uint64, len(elementArrayAsString)) + for index, stringElem := range elementArrayAsString { + int64Val, parseErr := strconv.ParseUint(stringElem, 10, 64) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an unsigned integer64 array", variableValue) + return + } + elementArrayAsInt[index] = int64Val + } + field.Set(reflect.ValueOf(elementArrayAsInt)) + case reflect.Float32: + elementArrayAsFloat := make([]float32, len(elementArrayAsString)) + for index, stringElem := range elementArrayAsString { + float64Val, parseErr := strconv.ParseFloat(stringElem, 32) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an float32 array", variableValue) + return + } + elementArrayAsFloat[index] = float32(float64Val) + } + field.Set(reflect.ValueOf(elementArrayAsFloat)) + case reflect.Float64: + elementArrayAsFloat := make([]float64, len(elementArrayAsString)) + var parseErr error + for index, stringElem := range elementArrayAsString { + elementArrayAsFloat[index], parseErr = strconv.ParseFloat(stringElem, 64) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as an float64 array", variableValue) + return + } + } + field.Set(reflect.ValueOf(elementArrayAsFloat)) + } + } +} + +func splitStringAndTrim(input string) []string { + ElementArrayAsString := strings.Split(input, ",") + for index := 0; index < len(ElementArrayAsString); index++ { + ElementArrayAsString[index] = strings.TrimSpace(ElementArrayAsString[index]) + } + return ElementArrayAsString +} + +func resolveEnvValueOfMap(field reflect.Value, currentTag string, resolveEnvTag bool) { + // Only map [string]string is supported here. + for _, key := range field.MapKeys() { + if field.MapIndex(key).Kind() == reflect.String && key.Kind() == reflect.String { + variableName := currentTag + envVariableEntrySeparator + key.String() + variableName = strings.ReplaceAll(variableName, ".", "_") + variableValue, exists := envVariableMap[envVariablePrefix+strings.ToUpper(variableName)] + if exists { + field.SetMapIndex(reflect.ValueOf(key.String()), reflect.ValueOf(variableValue)) + } + } + } +} + +// ResolveEnvValue replace the respective config values from environment variable. +func ResolveEnvValue(value string) string { + re := regexp.MustCompile(`(?s)\{(.*)}`) // regex to get everything in between curly brackets + m := re.FindStringSubmatch(value) + if len(m) > 1 { + envValue, exists := os.LookupEnv(m[1]) + if exists { + return strings.ReplaceAll(re.ReplaceAllString(value, envValue), EnvConfigPrefix, "") + } + } + return value +} +func resolveEnvStringValue(key string, value reflect.Value) { + variableValue, exists := envVariableMap[envVariablePrefix+strings.ToUpper(key)] + if exists { + value.SetString(variableValue) + } +} + +func resolveEnvBooleanValue(key string, value reflect.Value) { + var resolvedValue bool + var parseErr error + variableValue, exists := envVariableMap[envVariablePrefix+strings.ToUpper(key)] + if exists { + resolvedValue, parseErr = strconv.ParseBool(variableValue) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as a boolean value.", key) + return + } + value.SetBool(resolvedValue) + } +} + +func resolveEnvIntValue(key string, value reflect.Value) { + var resolvedValue int + var parseErr error + variableValue, exists := envVariableMap[envVariablePrefix+strings.ToUpper(key)] + if exists { + resolvedValue, parseErr = strconv.Atoi(variableValue) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as a int value. :", key) + return + } + value.SetInt(int64(resolvedValue)) + } +} + +func resolveEnvUIntValue(key string, value reflect.Value) { + var resolvedValue uint64 + var parseErr error + variableValue, exists := envVariableMap[envVariablePrefix+strings.ToUpper(key)] + if exists { + resolvedValue, parseErr = strconv.ParseUint(variableValue, 10, 32) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as a uint value.", key) + return + } + value.SetUint(resolvedValue) + } +} + +func resolveEnvFloat32Value(key string, value reflect.Value) { + var resolvedValue float64 + var parseErr error + variableValue, exists := envVariableMap[envVariablePrefix+strings.ToUpper(key)] + if exists { + resolvedValue, parseErr = strconv.ParseFloat(variableValue, 32) + if parseErr != nil { + loggerConfig.Logger.Errorf("Error while parsing %s as a float value.", key) + return + } + value.SetFloat(resolvedValue) + } +} diff --git a/common-controller/internal/config/types.go b/common-controller/internal/config/types.go index 58ae1161d..372dc195e 100644 --- a/common-controller/internal/config/types.go +++ b/common-controller/internal/config/types.go @@ -17,6 +17,8 @@ package config +import "time" + // Config represents the adapter configuration. // It is created directly from the configuration toml file. type Config struct { @@ -45,6 +47,19 @@ type commoncontroller struct { Sts sts WebServer webServer InternalAPIServer internalAPIServer + ControlPlane controlplane +} +type controlplane struct { + Enabled bool + Host string + EventPort int + RestPort int + RetryInterval time.Duration + Persistence persistence + SkipSSLVerification bool +} +type persistence struct { + Type string } type internalAPIServer struct { Port int64 @@ -59,7 +74,8 @@ type truststore struct { } type server struct { - Label string + Label string + ServerName string } type operator struct { diff --git a/common-controller/internal/controlplane/artifact_deployer.go b/common-controller/internal/controlplane/artifact_deployer.go new file mode 100644 index 000000000..838805fa5 --- /dev/null +++ b/common-controller/internal/controlplane/artifact_deployer.go @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package controlplane + +import "github.com/wso2/apk/common-controller/internal/server" + +// ArtifactDeployer is an interface that defines the methods that should be implemented by an artifact deployer +type ArtifactDeployer interface { + DeployApplication(application server.Application) error + UpdateApplication(application server.Application) error + DeploySubscription(subscription server.Subscription) error + UpdateSubscription(subscription server.Subscription) error + DeployApplicationMappings(applicationMapping server.ApplicationMapping) error + UpdateApplicationMappings(applicationMapping server.ApplicationMapping) error + DeployKeyMappings(keyMapping server.ApplicationKeyMapping) error + GetApplication(applicationID string) (server.Application, error) + GetSubscription(subscriptionID string) (server.Subscription, error) + GetApplicationMappings(applicationID string) (server.ApplicationMapping, error) + GetKeyMappings(applicationID string) (server.ApplicationKeyMapping, error) + GetAllApplications() (server.ApplicationList, error) + GetAllSubscriptions() (server.SubscriptionList, error) + GetAllApplicationMappings() (server.ApplicationMappingList, error) + GetAllKeyMappings() (server.ApplicationKeyMappingList, error) + DeleteApplication(applicationID string) error + DeleteSubscription(subscriptionID string) error + DeleteApplicationMappings(applicationID string) error + DeleteKeyMappings(keyMapping server.ApplicationKeyMapping) error + DeployAllApplications(applications server.ApplicationList) error + DeployAllSubscriptions(subscriptions server.SubscriptionList) error + DeployAllApplicationMappings(applicationMappings server.ApplicationMappingList) error + DeployAllKeyMappings(keyMappings server.ApplicationKeyMappingList) error +} diff --git a/common-controller/internal/controlplane/controlplane_client.go b/common-controller/internal/controlplane/controlplane_client.go new file mode 100644 index 000000000..cddf09833 --- /dev/null +++ b/common-controller/internal/controlplane/controlplane_client.go @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package controlplane + +import ( + "context" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "reflect" + "strconv" + "time" + + grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" + "github.com/wso2/apk/common-controller/internal/config" + "github.com/wso2/apk/common-controller/internal/loggers" + "github.com/wso2/apk/common-controller/internal/server" + "github.com/wso2/apk/common-controller/internal/utils" + "github.com/wso2/apk/common-go-libs/constants" + apkmgt "github.com/wso2/apk/common-go-libs/pkg/discovery/api/wso2/discovery/service/apkmgt" + "github.com/wso2/apk/common-go-libs/pkg/discovery/api/wso2/discovery/subscription" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/metadata" + grpcStatus "google.golang.org/grpc/status" +) + +// Agent is a struct that implements Agent interface +type Agent struct { + hostname string + port int + controlPlaneID string + artifactDeployer ArtifactDeployer +} + +var ( + subsriptionList *SubscriptionList + applicationList *ApplicationList + appKeyMappingList *ApplicationKeyMappingList + appMappingList *ApplicationMappingList + connectionFaultChannel chan bool + eventStreamingClient apkmgt.EventStreamService_StreamEventsClient + resources = []resource{ + { + endpoint: "/subscriptions", + responseType: subsriptionList, + }, + { + endpoint: "/applications", + responseType: applicationList, + }, + { + endpoint: "/applicationkeymappings", + responseType: appKeyMappingList, + }, + {endpoint: "/applicationmappings", + responseType: appMappingList, + }, + } +) + +func init() { + connectionFaultChannel = make(chan bool, 10) +} + +// NewControlPlaneAgent creates a new ControlPlaneAgent +func NewControlPlaneAgent(hostname string, port int, controlPlaneID string, artifactDeployer ArtifactDeployer) *Agent { + return &Agent{hostname: hostname, port: port, controlPlaneID: controlPlaneID, artifactDeployer: artifactDeployer} +} + +func (controlPlaneGrpcClient *Agent) initGrpcConnection() (*grpc.ClientConn, error) { + config := config.ReadConfigs() + publicKeyLocation, privateKeyLocation, truststoreLocation := utils.GetKeyLocations() + cert, err := utils.GetServerCertificate(publicKeyLocation, privateKeyLocation) + if err != nil { + return nil, err + } + caCertPool := utils.GetTrustedCertPool(truststoreLocation) + creds := credentials.NewTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + ServerName: config.CommonController.Server.ServerName, + }) + hostname := fmt.Sprintf("%s:%d", config.CommonController.ControlPlane.Host, config.CommonController.ControlPlane.EventPort) + backOff := grpc_retry.BackoffLinearWithJitter(config.CommonController.ControlPlane.RetryInterval*time.Second, 0.5) + conection, err := grpc.Dial(hostname, grpc.WithTransportCredentials(creds), grpc.WithStreamInterceptor( + grpc_retry.StreamClientInterceptor(grpc_retry.WithBackoff(backOff)))) + if err != nil { + loggers.LoggerAPKOperator.Errorf("Error while connecting to the control plane %s", err.Error()) + return nil, err + } + md := metadata.Pairs("common-controller-uuid", controlPlaneGrpcClient.controlPlaneID) + ctx := metadata.NewOutgoingContext(context.Background(), md) + client := apkmgt.NewEventStreamServiceClient(conection) + eventStreamingClient, err = client.StreamEvents(ctx, &apkmgt.Request{Event: controlPlaneGrpcClient.controlPlaneID}) + if err != nil { + loggers.LoggerAPKOperator.Errorf("Error while initializing streaming %s", err.Error()) + return nil, err + } + return conection, nil +} + +// StartEventStreaming starts event streaming +func (controlPlaneGrpcClient *Agent) StartEventStreaming() { + conn := controlPlaneGrpcClient.initializeGrpcStreaming() + for retryTrueReceived := range connectionFaultChannel { + // event is always true + if !retryTrueReceived { + continue + } + time.Sleep(config.ReadConfigs().CommonController.ControlPlane.RetryInterval * time.Second) + if conn != nil { + conn.Close() + } + loggers.LoggerAPKOperator.Error("Connection lost. Retrying to connect to the control plane") + conn = controlPlaneGrpcClient.initializeGrpcStreaming() + } +} + +// initializeGrpcStreaming starts event streaming +func (controlPlaneGrpcClient *Agent) initializeGrpcStreaming() *grpc.ClientConn { + conn, err := controlPlaneGrpcClient.initGrpcConnection() + if err != nil { + loggers.LoggerAPKOperator.Errorf("Error while initializing the connection. error: %s", err.Error()) + connectionFaultChannel <- true + return conn + } + go func() { + for { + resp, err := eventStreamingClient.Recv() + if err == io.EOF { + connectionFaultChannel <- true + return + } + if err != nil { + errStatus, _ := grpcStatus.FromError(err) + if errStatus.Code() == codes.Unavailable || + errStatus.Code() == codes.DeadlineExceeded || + errStatus.Code() == codes.Canceled || + errStatus.Code() == codes.ResourceExhausted || + errStatus.Code() == codes.Aborted || + errStatus.Code() == codes.Internal { + loggers.LoggerAPKOperator.Errorf("Connection unavailable. errorCode: %s errorMessage: %s", + errStatus.Code().String(), errStatus.Message()) + connectionFaultChannel <- true + } + return + } + controlPlaneGrpcClient.handleEvents(resp) + } + }() + return conn +} +func (controlPlaneGrpcClient *Agent) handleEvents(event *subscription.Event) { + loggers.LoggerAPKOperator.Infof("Received event %s", event.Type) + if event.Type == constants.AllEvnts { + go controlPlaneGrpcClient.retrieveAllData() + } else if event.Type == constants.ApplicationCreated { + loggers.LoggerAPKOperator.Infof("Received APPLICATION_CREATED event.") + if event.Application != nil { + application := server.Application{UUID: event.Application.Uuid, + Name: event.Application.Name, + Owner: event.Application.Owner, + OrganizationID: event.Application.Organization, + Attributes: event.Application.Attributes, + TimeStamp: event.TimeStamp, + } + loggers.LoggerAPKOperator.Infof("Received Application %s", application.UUID) + controlPlaneGrpcClient.artifactDeployer.DeployApplication(application) + } + } else if event.Type == constants.ApplicationUpdated { + if event.Application != nil { + application := server.Application{UUID: event.Application.Uuid, + Name: event.Application.Name, + Owner: event.Application.Owner, + OrganizationID: event.Application.Organization, + Attributes: event.Application.Attributes, + } + loggers.LoggerAPKOperator.Infof("Received Application %s", application.UUID) + controlPlaneGrpcClient.artifactDeployer.UpdateApplication(application) + } + } else if event.Type == constants.ApplicationDeleted { + if event.Application != nil { + application := server.Application{UUID: event.Application.Uuid, + Name: event.Application.Name, + Owner: event.Application.Owner, + OrganizationID: event.Application.Organization, + Attributes: event.Application.Attributes, + } + loggers.LoggerAPKOperator.Infof("Received Application %s", application.UUID) + controlPlaneGrpcClient.artifactDeployer.DeleteApplication(application.UUID) + } + } else if event.Type == constants.SubscriptionCreated { + loggers.LoggerAPKOperator.Infof("Received SUBSCRIPTION_CREATED event.") + if event.Subscription != nil { + subscription := server.Subscription{UUID: event.Subscription.Uuid, + Organization: event.Subscription.Organization, + SubStatus: event.Subscription.SubStatus, + SubscribedAPI: &server.SubscribedAPI{Name: event.Subscription.SubscribedApi.Name, Version: event.Subscription.SubscribedApi.Version}, + } + loggers.LoggerAPKOperator.Infof("Received Subscription %s", subscription.UUID) + controlPlaneGrpcClient.artifactDeployer.DeploySubscription(subscription) + } + } else if event.Type == constants.SubscriptionUpdated { + loggers.LoggerAPKOperator.Infof("Received SUBSCRIPTION_UPDATED event.") + if event.Subscription != nil { + subscription := server.Subscription{UUID: event.Subscription.Uuid, + Organization: event.Subscription.Organization, + SubStatus: event.Subscription.SubStatus, + SubscribedAPI: &server.SubscribedAPI{Name: event.Subscription.SubscribedApi.Name, Version: event.Subscription.SubscribedApi.Version}, + } + loggers.LoggerAPKOperator.Infof("Received Subscription %s", subscription.UUID) + controlPlaneGrpcClient.artifactDeployer.UpdateSubscription(subscription) + } + } else if event.Type == constants.SubscriptionDeleted { + loggers.LoggerAPKOperator.Infof("Received SUBSCRIPTION_DELETED event.") + if event.Subscription != nil { + subscription := server.Subscription{UUID: event.Subscription.Uuid, + Organization: event.Subscription.Organization, + SubStatus: event.Subscription.SubStatus, + SubscribedAPI: &server.SubscribedAPI{Name: event.Subscription.SubscribedApi.Name, Version: event.Subscription.SubscribedApi.Version}, + } + loggers.LoggerAPKOperator.Infof("Received Subscription %s", subscription.UUID) + controlPlaneGrpcClient.artifactDeployer.DeleteSubscription(subscription.UUID) + } + } else if event.Type == constants.ApplicationKeyMappingCreated { + loggers.LoggerAPKOperator.Infof("Received APPLICATION_KEY_MAPPING_CREATED event.") + if event.ApplicationKeyMapping != nil { + applicationKeyMapping := server.ApplicationKeyMapping{ApplicationUUID: event.ApplicationKeyMapping.ApplicationUUID, + SecurityScheme: event.ApplicationKeyMapping.SecurityScheme, + ApplicationIdentifier: event.ApplicationKeyMapping.ApplicationIdentifier, + KeyType: event.ApplicationKeyMapping.KeyType, + EnvID: event.ApplicationKeyMapping.EnvID, + OrganizationID: event.ApplicationKeyMapping.Organization, + } + loggers.LoggerAPKOperator.Infof("Received ApplicationKeyMapping %s", applicationKeyMapping.ApplicationUUID) + controlPlaneGrpcClient.artifactDeployer.DeployKeyMappings(applicationKeyMapping) + } + } else if event.Type == constants.ApplicationKeyMappingDeleted { + loggers.LoggerAPKOperator.Infof("Received APPLICATION_KEY_MAPPING_DELETED event.") + if event.ApplicationKeyMapping != nil { + applicationKeyMapping := server.ApplicationKeyMapping{ApplicationUUID: event.ApplicationKeyMapping.ApplicationUUID, + SecurityScheme: event.ApplicationKeyMapping.SecurityScheme, + ApplicationIdentifier: event.ApplicationKeyMapping.ApplicationIdentifier, + KeyType: event.ApplicationKeyMapping.KeyType, + EnvID: event.ApplicationKeyMapping.EnvID, + OrganizationID: event.ApplicationKeyMapping.Organization, + } + loggers.LoggerAPKOperator.Infof("Received ApplicationKeyMapping %s", applicationKeyMapping.ApplicationUUID) + controlPlaneGrpcClient.artifactDeployer.DeleteKeyMappings(applicationKeyMapping) + } + } else if event.Type == constants.ApplicationMappingCreated { + loggers.LoggerAPKOperator.Infof("Received APPLICATION_MAPPING_CREATED event.") + if event.ApplicationMapping != nil { + applicationMapping := server.ApplicationMapping{UUID: event.ApplicationMapping.Uuid, + ApplicationRef: event.ApplicationMapping.ApplicationRef, + SubscriptionRef: event.ApplicationMapping.SubscriptionRef, + OrganizationID: event.ApplicationMapping.Organization, + } + loggers.LoggerAPKOperator.Infof("Received ApplicationMapping %s", applicationMapping.UUID) + controlPlaneGrpcClient.artifactDeployer.DeployApplicationMappings(applicationMapping) + } + } else if event.Type == constants.ApplicationMappingDeleted { + loggers.LoggerAPKOperator.Infof("Received APPLICATION_MAPPING_DELETED event.") + if event.ApplicationMapping != nil { + applicationMapping := server.ApplicationMapping{UUID: event.ApplicationMapping.Uuid, + ApplicationRef: event.ApplicationMapping.ApplicationRef, + SubscriptionRef: event.ApplicationMapping.SubscriptionRef, + OrganizationID: event.ApplicationMapping.Organization, + } + loggers.LoggerAPKOperator.Infof("Received ApplicationMapping %s", applicationMapping.UUID) + controlPlaneGrpcClient.artifactDeployer.DeleteApplicationMappings(applicationMapping.UUID) + } + } else if event.Type == constants.ApplicationMappingUpdated { + loggers.LoggerAPKOperator.Infof("Received APPLICATION_MAPPING_UPDATED event.") + if event.ApplicationMapping != nil { + applicationMapping := server.ApplicationMapping{UUID: event.ApplicationMapping.Uuid, + ApplicationRef: event.ApplicationMapping.ApplicationRef, + SubscriptionRef: event.ApplicationMapping.SubscriptionRef, + OrganizationID: event.ApplicationMapping.Organization, + } + loggers.LoggerAPKOperator.Infof("Received ApplicationMapping %s", applicationMapping.UUID) + controlPlaneGrpcClient.artifactDeployer.UpdateApplicationMappings(applicationMapping) + } + + } +} +func (controlPlaneGrpcClient *Agent) retrieveAllData() { + var responseChannel = make(chan response) + config := config.ReadConfigs() + for _, url := range resources { + // Create a local copy of the loop variable + localURL := url + + go InvokeService(localURL.endpoint, localURL.responseType, nil, responseChannel, 0) + + for { + data := <-responseChannel + loggers.LoggerAPKOperator.Info("Receiving subscription data for an environment") + if data.Payload != nil { + loggers.LoggerAPKOperator.Info("Payload data information received" + string(data.Payload)) + controlPlaneGrpcClient.retrieveDataFromResponseChannel(data) + break + } else if data.ErrorCode >= 400 && data.ErrorCode < 500 { + //Error handle + loggers.LoggerAPKOperator.Info("Error data information received") + //health.SetControlPlaneRestAPIStatus(false) + } else { + // Keep the iteration going on until a response is received. + // Error handle + go func(d response, endpoint string, responseType interface{}) { + // Retry fetching from control plane after a configured time interval + if config.CommonController.ControlPlane.RetryInterval == 0 { + // Assign default retry interval + config.CommonController.ControlPlane.RetryInterval = 5 + } + loggers.LoggerAPKOperator.Debugf("Time Duration for retrying: %v", config.CommonController.ControlPlane.RetryInterval*time.Second) + time.Sleep(config.CommonController.ControlPlane.RetryInterval * time.Second) + loggers.LoggerAPKOperator.Infof("Retrying to fetch APIs from control plane. Time Duration for the next retry: %v", config.CommonController.ControlPlane.RetryInterval*time.Second) + go InvokeService(endpoint, responseType, nil, responseChannel, 0) + }(data, localURL.endpoint, localURL.responseType) + } + } + } +} + +type resource struct { + endpoint string + responseType interface{} +} + +type response struct { + Error error + Payload []byte + ErrorCode int + Endpoint string + Type interface{} +} + +// InvokeService invokes the internal data resource +func InvokeService(endpoint string, responseType interface{}, queryParamMap map[string]string, c chan response, + retryAttempt int) { + config := config.ReadConfigs() + serviceURL := "https://" + config.CommonController.ControlPlane.Host + ":" + strconv.Itoa(config.CommonController.ControlPlane.RestPort) + endpoint + // Create the request + req, err := http.NewRequest("GET", serviceURL, nil) + if err != nil { + c <- response{err, nil, 0, endpoint, responseType} + loggers.LoggerAPKOperator.Errorf("Error occurred while creating an HTTP request for serviceURL: "+serviceURL, err) + return + } + q := req.URL.Query() + req.URL.RawQuery = q.Encode() + + // Check if TLS is enabled + skipSSL := config.CommonController.ControlPlane.SkipSSLVerification + resp, err := InvokeControlPlane(req, skipSSL) + + if err != nil { + if resp != nil { + c <- response{err, nil, resp.StatusCode, endpoint, responseType} + } else { + c <- response{err, nil, 0, endpoint, responseType} + } + loggers.LoggerAPKOperator.Infof("Error occurred while calling the REST API: "+serviceURL, err) + return + } + + responseBytes, err := ioutil.ReadAll(resp.Body) + if resp.StatusCode == http.StatusOK { + if err != nil { + c <- response{err, nil, resp.StatusCode, endpoint, responseType} + loggers.LoggerAPKOperator.Infof("Error occurred while reading the response received for: "+serviceURL, err) + return + } + c <- response{nil, responseBytes, resp.StatusCode, endpoint, responseType} + } else { + c <- response{errors.New(string(responseBytes)), nil, resp.StatusCode, endpoint, responseType} + loggers.LoggerAPKOperator.Infof("Failed to fetch data! "+serviceURL+" responded with "+strconv.Itoa(resp.StatusCode), + err) + } +} + +// InvokeControlPlane sends request to the control plane and returns the response +func InvokeControlPlane(req *http.Request, skipSSL bool) (*http.Response, error) { + tr := &http.Transport{} + if !skipSSL { + _, _, truststoreLocation := utils.GetKeyLocations() + caCertPool := utils.GetTrustedCertPool(truststoreLocation) + tr = &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: caCertPool}, + } + } else { + tr = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + } + + // Configuring the http client + client := &http.Client{ + Transport: tr, + } + return client.Do(req) +} +func (controlPlaneGrpcClient *Agent) retrieveDataFromResponseChannel(response response) { + responseType := reflect.TypeOf(response.Type).Elem() + newResponse := reflect.New(responseType).Interface() + err := json.Unmarshal(response.Payload, &newResponse) + + if err != nil { + loggers.LoggerAPI.Infof("Error occurred while unmarshalling the response received for: "+response.Endpoint, err) + } else { + switch t := newResponse.(type) { + case *SubscriptionList: + loggers.LoggerAPI.Infof("Received Subscription information.") + subList := newResponse.(*SubscriptionList) + resolvedSubscriptionList := marshalMultipleSubscriptions(subList) + controlPlaneGrpcClient.artifactDeployer.DeployAllSubscriptions(resolvedSubscriptionList) + + case *ApplicationList: + loggers.LoggerAPI.Infof("Received Application information.") + appList := newResponse.(*ApplicationList) + resolvedApplicationList := marshalMultipleApplications(appList) + controlPlaneGrpcClient.artifactDeployer.DeployAllApplications(resolvedApplicationList) + case *ApplicationKeyMappingList: + loggers.LoggerAPI.Infof("Received Application Key Mapping information.") + appKeyMappingList := newResponse.(*ApplicationKeyMappingList) + resolvedApplicationKeyMappingList := marshalMultipleApplicationKeyMappings(appKeyMappingList) + controlPlaneGrpcClient.artifactDeployer.DeployAllKeyMappings(resolvedApplicationKeyMappingList) + case *ApplicationMappingList: + loggers.LoggerAPI.Infof("Received Application Mapping information.") + appMappingList := newResponse.(*ApplicationMappingList) + resolvedApplicationMappingList := marshalMultipleApplicationMappings(appMappingList) + controlPlaneGrpcClient.artifactDeployer.DeployAllApplicationMappings(resolvedApplicationMappingList) + default: + loggers.LoggerAPI.Debugf("Unknown type %T", t) + } + } +} +func marshalMultipleSubscriptions(subList *SubscriptionList) server.SubscriptionList { + subscriptionList := server.SubscriptionList{List: []server.Subscription{}} + for _, subscription := range subList.List { + loggers.LoggerAPI.Debugf("Subscription: %v", subscription) + subscriptionList.List = append(subscriptionList.List, server.Subscription{UUID: subscription.UUID, Organization: subscription.Organization, SubStatus: subscription.SubStatus, SubscribedAPI: &server.SubscribedAPI{Name: subscription.SubscribedAPI.Name, Version: subscription.SubscribedAPI.Version}}) + } + return subscriptionList +} +func marshalMultipleApplications(appList *ApplicationList) server.ApplicationList { + applicationList := server.ApplicationList{List: []server.Application{}} + for _, application := range appList.List { + loggers.LoggerAPI.Debugf("Application: %v", application) + applicationList.List = append(applicationList.List, server.Application{UUID: application.UUID, Name: application.Name, Owner: application.Owner, OrganizationID: application.Organization, Attributes: application.Attributes}) + } + return applicationList +} +func marshalMultipleApplicationKeyMappings(appKeyMappingList *ApplicationKeyMappingList) server.ApplicationKeyMappingList { + applicationKeyMappingList := server.ApplicationKeyMappingList{List: []server.ApplicationKeyMapping{}} + for _, applicationKeyMapping := range appKeyMappingList.List { + loggers.LoggerAPI.Debugf("ApplicationKeyMapping: %v", applicationKeyMapping) + applicationKeyMappingList.List = append(applicationKeyMappingList.List, server.ApplicationKeyMapping{ApplicationUUID: applicationKeyMapping.ApplicationUUID, SecurityScheme: applicationKeyMapping.SecurityScheme, ApplicationIdentifier: applicationKeyMapping.ApplicationIdentifier, KeyType: applicationKeyMapping.KeyType, EnvID: applicationKeyMapping.EnvID, OrganizationID: applicationKeyMapping.Organization}) + } + return applicationKeyMappingList +} +func marshalMultipleApplicationMappings(appMappingList *ApplicationMappingList) server.ApplicationMappingList { + applicationMappingList := server.ApplicationMappingList{List: []server.ApplicationMapping{}} + for _, applicationMapping := range appMappingList.List { + loggers.LoggerAPI.Debugf("ApplicationMapping: %v", applicationMapping) + applicationMappingList.List = append(applicationMappingList.List, server.ApplicationMapping{UUID: applicationMapping.UUID, ApplicationRef: applicationMapping.ApplicationRef, SubscriptionRef: applicationMapping.SubscriptionRef, OrganizationID: applicationMapping.Organization}) + } + return applicationMappingList +} diff --git a/common-controller/internal/controlplane/k8s_artifact_deployer.go b/common-controller/internal/controlplane/k8s_artifact_deployer.go new file mode 100644 index 000000000..d5f61f185 --- /dev/null +++ b/common-controller/internal/controlplane/k8s_artifact_deployer.go @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package controlplane + +import ( + "context" + "strconv" + + "github.com/wso2/apk/adapter/pkg/logging" + "github.com/wso2/apk/common-controller/internal/loggers" + "github.com/wso2/apk/common-controller/internal/server" + cpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha2" + "github.com/wso2/apk/common-go-libs/utils" + k8error "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +const ( + // CreationTimeStamp constant for annotation creationTimeStamp + CreationTimeStamp = "creationTimeStamp" +) + +// K8sArtifactDeployer is a struct that implements ArtifactDeployer interface +type K8sArtifactDeployer struct { + client client.Client +} + +// NewK8sArtifactDeployer creates a new K8sArtifactDeployer +func NewK8sArtifactDeployer(mgr manager.Manager) K8sArtifactDeployer { + return K8sArtifactDeployer{client: mgr.GetClient()} +} + +// DeployApplication deploys an application +func (k8sArtifactDeployer K8sArtifactDeployer) DeployApplication(application server.Application) error { + crApplication := cpv1alpha2.Application{ + ObjectMeta: v1.ObjectMeta{ + Name: application.UUID, + Namespace: utils.GetOperatorPodNamespace(), + Labels: map[string]string{CreationTimeStamp: strconv.FormatInt(application.TimeStamp, 10)}, + }, + Spec: cpv1alpha2.ApplicationSpec{ + Name: application.Name, + Owner: application.Owner, + Organization: application.OrganizationID, + Attributes: application.Attributes, + }, + } + loggers.LoggerAPKOperator.Debugf("Creating Application %s", application.UUID) + loggers.LoggerAPKOperator.Debugf("Application CR %v ", crApplication) + err := k8sArtifactDeployer.client.Create(context.Background(), &crApplication) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to create application in k8s %v", err.Error())) + return err + } + return nil +} + +// UpdateApplication updates an application +func (k8sArtifactDeployer K8sArtifactDeployer) UpdateApplication(application server.Application) error { + crApplication := cpv1alpha2.Application{} + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: application.UUID, Namespace: utils.GetOperatorPodNamespace()}, &crApplication) + if err != nil { + if !k8error.IsNotFound(err) { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return err + } + k8sArtifactDeployer.DeployApplication(application) + } else { + crApplication.Spec.Name = application.Name + crApplication.Spec.Owner = application.Owner + crApplication.Spec.Organization = application.OrganizationID + crApplication.Spec.Attributes = application.Attributes + err := k8sArtifactDeployer.client.Update(context.Background(), &crApplication) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to update application in k8s %v", err.Error())) + return err + } + } + + return nil +} + +// DeploySubscription deploys a subscription +func (k8sArtifactDeployer K8sArtifactDeployer) DeploySubscription(subscription server.Subscription) error { + crSubscription := cpv1alpha2.Subscription{ObjectMeta: v1.ObjectMeta{Name: subscription.UUID, Namespace: utils.GetOperatorPodNamespace()}, + Spec: cpv1alpha2.SubscriptionSpec{Organization: subscription.Organization, API: cpv1alpha2.API{Name: subscription.SubscribedAPI.Name, Version: subscription.SubscribedAPI.Version}, SubscriptionStatus: subscription.SubStatus}} + err := k8sArtifactDeployer.client.Create(context.Background(), &crSubscription) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1101, logging.CRITICAL, "Failed to create subscription in k8s %v", err.Error())) + return err + } + return nil +} + +// UpdateSubscription updates a subscription +func (k8sArtifactDeployer K8sArtifactDeployer) UpdateSubscription(subscription server.Subscription) error { + crSubscription := cpv1alpha2.Subscription{} + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: subscription.UUID, Namespace: utils.GetOperatorPodNamespace()}, &crSubscription) + if err != nil { + if !k8error.IsNotFound(err) { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get subscription from k8s %v", err.Error())) + return err + } + k8sArtifactDeployer.DeploySubscription(subscription) + } else { + crSubscription.Spec.Organization = subscription.Organization + crSubscription.Spec.API.Name = subscription.SubscribedAPI.Name + crSubscription.Spec.API.Version = subscription.SubscribedAPI.Version + crSubscription.Spec.SubscriptionStatus = subscription.SubStatus + err := k8sArtifactDeployer.client.Update(context.Background(), &crSubscription) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to update subscription in k8s %v", err.Error())) + return err + } + } + return nil +} + +// DeployApplicationMappings deploys an application mapping +func (k8sArtifactDeployer K8sArtifactDeployer) DeployApplicationMappings(applicationMapping server.ApplicationMapping) error { + crApplicationMapping := cpv1alpha2.ApplicationMapping{ObjectMeta: v1.ObjectMeta{Name: applicationMapping.UUID, Namespace: utils.GetOperatorPodNamespace()}, + Spec: cpv1alpha2.ApplicationMappingSpec{ApplicationRef: applicationMapping.ApplicationRef, SubscriptionRef: applicationMapping.SubscriptionRef}} + return k8sArtifactDeployer.client.Create(context.Background(), &crApplicationMapping) +} + +// DeployKeyMappings deploys a key mapping +func (k8sArtifactDeployer K8sArtifactDeployer) DeployKeyMappings(keyMapping server.ApplicationKeyMapping) error { + var crApplication cpv1alpha2.Application + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: keyMapping.ApplicationUUID, Namespace: utils.GetOperatorPodNamespace()}, &crApplication) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return err + } + securitySchemes := cpv1alpha2.SecuritySchemes{} + if crApplication.Spec.SecuritySchemes != nil { + securitySchemes = *crApplication.Spec.SecuritySchemes + } + if keyMapping.SecurityScheme == "OAuth2" { + if securitySchemes.OAuth2 == nil { + securitySchemes.OAuth2 = &cpv1alpha2.SecurityScheme{Environments: []cpv1alpha2.Environment{generateSecurityScheme(keyMapping)}} + } else { + environments := make([]cpv1alpha2.Environment, 0) + for _, environment := range securitySchemes.OAuth2.Environments { + if environment.EnvID != keyMapping.EnvID || environment.AppID != keyMapping.ApplicationIdentifier || environment.KeyType != keyMapping.KeyType { + environments = append(environments, environment) + } + } + securitySchemes.OAuth2.Environments = append(environments, generateSecurityScheme(keyMapping)) + } + } + crApplication.Spec.SecuritySchemes = &securitySchemes + loggers.LoggerAPKOperator.Infof("Updating Application %v", crApplication) + return k8sArtifactDeployer.client.Update(context.Background(), &crApplication) +} + +// DeleteAllApplicationMappings deletes all application mappings +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteAllApplicationMappings() error { + return nil +} + +// DeleteAllKeyMappings deletes all key mappings +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteAllKeyMappings() error { + return nil +} + +// DeleteAllSubscriptions deletes all subscriptions +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteAllSubscriptions() error { + return nil +} + +// DeleteApplication deletes an application +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteApplication(applicationID string) error { + crApplication := cpv1alpha2.Application{} + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: applicationID, Namespace: utils.GetOperatorPodNamespace()}, &crApplication) + if err != nil { + if !k8error.IsNotFound(err) { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return err + } + } else { + err := k8sArtifactDeployer.client.Delete(context.Background(), &crApplication) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to delete application in k8s %v", err.Error())) + return err + } + } + + return nil +} + +// DeleteApplicationMappings deletes an application mapping +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteApplicationMappings(applicationMapping string) error { + crApplicationMapping := cpv1alpha2.ApplicationMapping{} + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: applicationMapping, Namespace: utils.GetOperatorPodNamespace()}, &crApplicationMapping) + if err != nil { + if !k8error.IsNotFound(err) { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application mapping from k8s %v", err.Error())) + return err + } + } else { + err := k8sArtifactDeployer.client.Delete(context.Background(), &crApplicationMapping) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to delete application mapping in k8s %v", err.Error())) + return err + } + } + + return nil +} + +// UpdateApplicationMappings updates an application mapping +func (k8sArtifactDeployer K8sArtifactDeployer) UpdateApplicationMappings(applicationMapping server.ApplicationMapping) error { + crApplicationMapping := cpv1alpha2.ApplicationMapping{} + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: applicationMapping.UUID, Namespace: utils.GetOperatorPodNamespace()}, &crApplicationMapping) + if err != nil { + if !k8error.IsNotFound(err) { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application mapping from k8s %v", err.Error())) + return err + } + k8sArtifactDeployer.DeployApplicationMappings(applicationMapping) + } else { + crApplicationMapping.Spec.ApplicationRef = applicationMapping.ApplicationRef + crApplicationMapping.Spec.SubscriptionRef = applicationMapping.SubscriptionRef + err := k8sArtifactDeployer.client.Update(context.Background(), &crApplicationMapping) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to update application mapping in k8s %v", err.Error())) + return err + } + } + + return nil +} + +// DeleteKeyMappings deletes a key mapping +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteKeyMappings(keyMapping server.ApplicationKeyMapping) error { + var crApplication cpv1alpha2.Application + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: keyMapping.ApplicationUUID, Namespace: utils.GetOperatorPodNamespace()}, &crApplication) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return err + } + if crApplication.Spec.SecuritySchemes != nil { + securitySchemes := *crApplication.Spec.SecuritySchemes + if keyMapping.SecurityScheme == "OAuth2" && securitySchemes.OAuth2 != nil { + if securitySchemes.OAuth2.Environments != nil && len(securitySchemes.OAuth2.Environments) > 0 { + environments := make([]cpv1alpha2.Environment, 0) + for _, environment := range securitySchemes.OAuth2.Environments { + if environment.EnvID != keyMapping.EnvID || environment.AppID != keyMapping.ApplicationIdentifier { + environments = append(environments, environment) + } + } + securitySchemes.OAuth2.Environments = environments + } + } + crApplication.Spec.SecuritySchemes = &securitySchemes + loggers.LoggerAPKOperator.Infof("Updating Application %v", crApplication) + return k8sArtifactDeployer.client.Update(context.Background(), &crApplication) + } + return nil +} + +// DeleteSubscription deletes a subscription +func (k8sArtifactDeployer K8sArtifactDeployer) DeleteSubscription(subscriptionID string) error { + crSubscription := cpv1alpha2.Subscription{} + err := k8sArtifactDeployer.client.Get(context.Background(), client.ObjectKey{Name: subscriptionID, Namespace: utils.GetOperatorPodNamespace()}, &crSubscription) + if err != nil { + if !k8error.IsNotFound(err) { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get subscription from k8s %v", err.Error())) + return err + } + } else { + err := k8sArtifactDeployer.client.Delete(context.Background(), &crSubscription) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1100, logging.CRITICAL, "Failed to delete subscription in k8s %v", err.Error())) + return err + } + } + + return nil +} + +// DeployAllApplicationMappings deploys all application mappings +func (k8sArtifactDeployer K8sArtifactDeployer) DeployAllApplicationMappings(applicationMappings server.ApplicationMappingList) error { + applicationMappingsFromK8s, _, err := k8sArtifactDeployer.retrieveAllApplicationMappings("") + if err != nil { + return err + } + clonedApplicationMappingsFromK8s := make([]cpv1alpha2.ApplicationMapping, len(applicationMappingsFromK8s)) + copy(clonedApplicationMappingsFromK8s, applicationMappingsFromK8s) + clonedApplicationMappings := make([]server.ApplicationMapping, len(applicationMappings.List)) + copy(clonedApplicationMappings, applicationMappings.List) + newApplicationMappings := make([]server.ApplicationMapping, 0) + sameApplicationMappings := make([]server.ApplicationMapping, 0) + for _, applicationMapping := range clonedApplicationMappings { + found := false + unFilteredApplicationMappingsInK8s := make([]cpv1alpha2.ApplicationMapping, 0) + for _, applicationMappingFromK8s := range clonedApplicationMappingsFromK8s { + if applicationMapping.ApplicationRef == applicationMappingFromK8s.Spec.ApplicationRef && applicationMapping.SubscriptionRef == applicationMappingFromK8s.Spec.SubscriptionRef { + sameApplicationMappings = append(sameApplicationMappings, applicationMapping) + found = true + break + } + unFilteredApplicationMappingsInK8s = append(unFilteredApplicationMappingsInK8s, applicationMappingFromK8s) + } + clonedApplicationMappingsFromK8s = unFilteredApplicationMappingsInK8s + if !found { + newApplicationMappings = append(newApplicationMappings, applicationMapping) + } + } + for _, applicationMapping := range newApplicationMappings { + err := k8sArtifactDeployer.DeployApplicationMappings(applicationMapping) + if err != nil { + return err + } + } + for _, applicationMapping := range sameApplicationMappings { + err := k8sArtifactDeployer.UpdateApplicationMappings(applicationMapping) + if err != nil { + return err + } + } + for _, applicationMappingFromK8s := range clonedApplicationMappingsFromK8s { + err := k8sArtifactDeployer.DeleteApplicationMappings(applicationMappingFromK8s.Name) + if err != nil { + return err + } + } + return nil +} + +// DeployAllApplications deploys all key mappings +func (k8sArtifactDeployer K8sArtifactDeployer) DeployAllApplications(applications server.ApplicationList) error { + applicationsFromK8s, _, err := k8sArtifactDeployer.retrieveAllApplicationsFromK8s("") + if err != nil { + return err + } + clonedApplicationsFromK8s := make([]cpv1alpha2.Application, len(applicationsFromK8s)) + copy(clonedApplicationsFromK8s, applicationsFromK8s) + clonedApplications := make([]server.Application, len(applications.List)) + copy(clonedApplications, applications.List) + newApplications := make([]server.Application, 0) + sameApplications := make([]server.Application, 0) + for _, application := range clonedApplications { + found := false + unFilteredApplicationsInK8s := make([]cpv1alpha2.Application, 0) + for _, applicationFromK8s := range clonedApplicationsFromK8s { + if application.UUID == applicationFromK8s.Name { + sameApplications = append(sameApplications, application) + found = true + break + } + unFilteredApplicationsInK8s = append(unFilteredApplicationsInK8s, applicationFromK8s) + } + clonedApplicationsFromK8s = unFilteredApplicationsInK8s + if !found { + newApplications = append(newApplications, application) + } + } + for _, application := range newApplications { + err := k8sArtifactDeployer.DeployApplication(application) + if err != nil { + return err + } + } + for _, application := range sameApplications { + err := k8sArtifactDeployer.UpdateApplication(application) + if err != nil { + return err + } + } + for _, applicationFromK8s := range clonedApplicationsFromK8s { + err := k8sArtifactDeployer.DeleteApplication(applicationFromK8s.Name) + if err != nil { + return err + } + } + return nil +} + +// DeployAllKeyMappings deploys all key mappings +func (k8sArtifactDeployer K8sArtifactDeployer) DeployAllKeyMappings(keyMappings server.ApplicationKeyMappingList) error { + return nil +} + +// DeployAllSubscriptions deploys all subscriptions +func (k8sArtifactDeployer K8sArtifactDeployer) DeployAllSubscriptions(subscriptions server.SubscriptionList) error { + subscriptionsFromK8s, _, err := k8sArtifactDeployer.retrieveAllSubscriptionsFromK8s("") + if err != nil { + return err + } + clonedSubscriptionsFromK8s := make([]cpv1alpha2.Subscription, len(subscriptionsFromK8s)) + copy(clonedSubscriptionsFromK8s, subscriptionsFromK8s) + clonedSubscriptions := make([]server.Subscription, len(subscriptions.List)) + copy(clonedSubscriptions, subscriptions.List) + newSubscriptions := make([]server.Subscription, 0) + sameSubscriptions := make([]server.Subscription, 0) + for _, subscription := range clonedSubscriptions { + found := false + unFilteredSubscriptionsInK8s := make([]cpv1alpha2.Subscription, 0) + for _, subscriptionFromK8s := range clonedSubscriptionsFromK8s { + if subscription.UUID == subscriptionFromK8s.Name { + sameSubscriptions = append(sameSubscriptions, subscription) + found = true + break + } + unFilteredSubscriptionsInK8s = append(unFilteredSubscriptionsInK8s, subscriptionFromK8s) + } + clonedSubscriptionsFromK8s = unFilteredSubscriptionsInK8s + if !found { + newSubscriptions = append(newSubscriptions, subscription) + } + } + for _, subscription := range newSubscriptions { + err := k8sArtifactDeployer.DeploySubscription(subscription) + if err != nil { + return err + } + } + for _, subscription := range sameSubscriptions { + err := k8sArtifactDeployer.UpdateSubscription(subscription) + if err != nil { + return err + } + } + for _, subscriptionFromK8s := range clonedSubscriptionsFromK8s { + err := k8sArtifactDeployer.DeleteSubscription(subscriptionFromK8s.Name) + if err != nil { + return err + } + } + return nil +} + +// GetAllApplicationMappings returns all application mappings +func (k8sArtifactDeployer K8sArtifactDeployer) GetAllApplicationMappings() (server.ApplicationMappingList, error) { + return server.ApplicationMappingList{}, nil +} + +// GetAllApplications returns all applications +func (k8sArtifactDeployer K8sArtifactDeployer) GetAllApplications() (server.ApplicationList, error) { + return server.ApplicationList{}, nil +} + +// GetAllKeyMappings returns all key mappings +func (k8sArtifactDeployer K8sArtifactDeployer) GetAllKeyMappings() (server.ApplicationKeyMappingList, error) { + return server.ApplicationKeyMappingList{}, nil +} + +// GetAllSubscriptions returns all subscriptions +func (k8sArtifactDeployer K8sArtifactDeployer) GetAllSubscriptions() (server.SubscriptionList, error) { + return server.SubscriptionList{}, nil +} + +// GetApplication returns an application +func (k8sArtifactDeployer K8sArtifactDeployer) GetApplication(applicationID string) (server.Application, error) { + return server.Application{}, nil +} + +// GetApplicationMappings returns an application mapping +func (k8sArtifactDeployer K8sArtifactDeployer) GetApplicationMappings(applicationID string) (server.ApplicationMapping, error) { + return server.ApplicationMapping{}, nil +} + +// GetKeyMappings returns a key mapping +func (k8sArtifactDeployer K8sArtifactDeployer) GetKeyMappings(applicationID string) (server.ApplicationKeyMapping, error) { + return server.ApplicationKeyMapping{}, nil +} + +// GetSubscription returns a subscription +func (k8sArtifactDeployer K8sArtifactDeployer) GetSubscription(subscriptionID string) (server.Subscription, error) { + return server.Subscription{}, nil +} + +// GenerateSecurityScheme generates a security scheme +func generateSecurityScheme(keyMapping server.ApplicationKeyMapping) cpv1alpha2.Environment { + return cpv1alpha2.Environment{EnvID: keyMapping.EnvID, AppID: keyMapping.ApplicationIdentifier, KeyType: keyMapping.KeyType} +} + +func (k8sArtifactDeployer K8sArtifactDeployer) retrieveAllApplicationsFromK8s(nextToken string) ([]cpv1alpha2.Application, string, error) { + applicationList := cpv1alpha2.ApplicationList{} + resolvedApplicationList := make([]cpv1alpha2.Application, 0) + var err error + if nextToken == "" { + err = k8sArtifactDeployer.client.List(context.Background(), &applicationList, &client.ListOptions{Namespace: utils.GetOperatorPodNamespace()}) + } else { + err = k8sArtifactDeployer.client.List(context.Background(), &applicationList, &client.ListOptions{Namespace: utils.GetOperatorPodNamespace(), Continue: nextToken}) + } + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return nil, "", err + } + resolvedApplicationList = append(resolvedApplicationList, applicationList.Items...) + if applicationList.Continue != "" { + tempApplicationList, _, err := k8sArtifactDeployer.retrieveAllApplicationsFromK8s(applicationList.Continue) + if err != nil { + return nil, "", err + } + resolvedApplicationList = append(resolvedApplicationList, tempApplicationList...) + } + return resolvedApplicationList, applicationList.Continue, nil +} + +func (k8sArtifactDeployer K8sArtifactDeployer) retrieveAllSubscriptionsFromK8s(nextToken string) ([]cpv1alpha2.Subscription, string, error) { + subscriptionList := cpv1alpha2.SubscriptionList{} + resolvedSubscripitonList := make([]cpv1alpha2.Subscription, 0) + var err error + if nextToken == "" { + err = k8sArtifactDeployer.client.List(context.Background(), &subscriptionList, &client.ListOptions{Namespace: utils.GetOperatorPodNamespace()}) + } else { + err = k8sArtifactDeployer.client.List(context.Background(), &subscriptionList, &client.ListOptions{Namespace: utils.GetOperatorPodNamespace(), Continue: nextToken}) + } + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return nil, "", err + } + resolvedSubscripitonList = append(resolvedSubscripitonList, subscriptionList.Items...) + if subscriptionList.Continue != "" { + tempSubscriptipnList, _, err := k8sArtifactDeployer.retrieveAllSubscriptionsFromK8s(subscriptionList.Continue) + if err != nil { + return nil, "", err + } + resolvedSubscripitonList = append(resolvedSubscripitonList, tempSubscriptipnList...) + } + return resolvedSubscripitonList, subscriptionList.Continue, nil +} +func (k8sArtifactDeployer K8sArtifactDeployer) retrieveAllApplicationMappings(nextToken string) ([]cpv1alpha2.ApplicationMapping, string, error) { + applicationMappingList := cpv1alpha2.ApplicationMappingList{} + resolvedApplicationMappingList := make([]cpv1alpha2.ApplicationMapping, 0) + var err error + if nextToken == "" { + err = k8sArtifactDeployer.client.List(context.Background(), &applicationMappingList, &client.ListOptions{Namespace: utils.GetOperatorPodNamespace()}) + } else { + err = k8sArtifactDeployer.client.List(context.Background(), &applicationMappingList, &client.ListOptions{Namespace: utils.GetOperatorPodNamespace(), Continue: nextToken}) + } + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error1102, logging.CRITICAL, "Failed to get application from k8s %v", err.Error())) + return nil, "", err + } + resolvedApplicationMappingList = append(resolvedApplicationMappingList, applicationMappingList.Items...) + if applicationMappingList.Continue != "" { + tempApplicationMappingList, _, err := k8sArtifactDeployer.retrieveAllApplicationMappings(applicationMappingList.Continue) + if err != nil { + return nil, "", err + } + resolvedApplicationMappingList = append(resolvedApplicationMappingList, tempApplicationMappingList...) + } + return resolvedApplicationMappingList, applicationMappingList.Continue, nil +} diff --git a/common-controller/internal/controlplane/types.go b/common-controller/internal/controlplane/types.go new file mode 100644 index 000000000..da9a987ff --- /dev/null +++ b/common-controller/internal/controlplane/types.go @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package controlplane + +// Subscription for struct subscription +type Subscription struct { + SubStatus string `json:"subStatus,omitempty"` + UUID string `json:"uuid,omitempty"` + Organization string `json:"organization,omitempty"` + SubscribedAPI *SubscribedAPI `json:"subscribedApi,omitempty"` + TimeStamp int64 `json:"timeStamp,omitempty"` +} + +// SubscriptionList for struct list of applications +type SubscriptionList struct { + List []Subscription `json:"list"` +} + +// SubscribedAPI for struct subscribedAPI +type SubscribedAPI struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` +} + +// Application for struct application +type Application struct { + UUID string `json:"uuid,omitempty"` + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Organization string `json:"organization,omitempty"` + Attributes map[string]string `json:"attributes,omitempty"` + TimeStamp int64 `json:"timeStamp,omitempty"` +} + +// ApplicationList for struct list of application +type ApplicationList struct { + List []Application `json:"list"` +} + +// ApplicationKeyMapping for struct applicationKeyMapping +type ApplicationKeyMapping struct { + ApplicationUUID string `json:"applicationUUID,omitempty"` + SecurityScheme string `json:"securityScheme,omitempty"` + ApplicationIdentifier string `json:"applicationIdentifier,omitempty"` + KeyType string `json:"keyType,omitempty"` + EnvID string `json:"envID,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + Organization string `json:"organization,omitempty"` +} + +// ApplicationKeyMappingList for struct list of applicationKeyMapping +type ApplicationKeyMappingList struct { + List []ApplicationKeyMapping `json:"list"` +} + +// ApplicationMapping for struct applicationMapping +type ApplicationMapping struct { + UUID string `json:"uuid,omitempty"` + ApplicationRef string `json:"applicationRef,omitempty"` + SubscriptionRef string `json:"subscriptionRef,omitempty"` + Organization string `json:"organization,omitempty"` +} + +// ApplicationMappingList for struct list of applicationMapping +type ApplicationMappingList struct { + List []ApplicationMapping `json:"list"` +} diff --git a/common-controller/internal/operator/controllers/cp/application_controller.go b/common-controller/internal/operator/controllers/cp/application_controller.go index 826825e04..a55cab1ec 100644 --- a/common-controller/internal/operator/controllers/cp/application_controller.go +++ b/common-controller/internal/operator/controllers/cp/application_controller.go @@ -123,9 +123,11 @@ func (applicationReconciler *ApplicationReconciler) sendAppUpdates(application c server.DeleteApplication(application.Name) } server.AddApplication(resolvedApplication) - appKeyMappingList := marshalApplicationKeyMapping(application) - for _, applicationKeyMapping := range appKeyMappingList { - server.AddApplicationKeyMapping(applicationKeyMapping) + if application.Spec.SecuritySchemes != nil { + appKeyMappingList := marshalApplicationKeyMapping(application) + for _, applicationKeyMapping := range appKeyMappingList { + server.AddApplicationKeyMapping(applicationKeyMapping) + } } } diff --git a/common-controller/internal/operator/operator.go b/common-controller/internal/operator/operator.go index 73a252074..e0f09b384 100644 --- a/common-controller/internal/operator/operator.go +++ b/common-controller/internal/operator/operator.go @@ -24,9 +24,17 @@ import ( // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + "github.com/google/uuid" "github.com/wso2/apk/adapter/pkg/logging" cache "github.com/wso2/apk/common-controller/internal/cache" + "github.com/wso2/apk/common-controller/internal/config" + "github.com/wso2/apk/common-controller/internal/controlplane" "github.com/wso2/apk/common-controller/internal/loggers" + cpcontrollers "github.com/wso2/apk/common-controller/internal/operator/controllers/cp" + dpcontrollers "github.com/wso2/apk/common-controller/internal/operator/controllers/dp" + cpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha2" + dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" + dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -34,12 +42,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - cpcontrollers "github.com/wso2/apk/common-controller/internal/operator/controllers/cp" - dpcontrollers "github.com/wso2/apk/common-controller/internal/operator/controllers/dp" - cpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha2" - dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" - dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" //+kubebuilder:scaffold:imports ) @@ -63,6 +65,7 @@ func InitOperator() { var metricsAddr string var enableLeaderElection bool var probeAddr string + controlPlaneID := uuid.New().String() flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, @@ -161,6 +164,20 @@ func InitOperator() { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2603, logging.BLOCKER, "Unable to set up ready check: %v", err)) os.Exit(1) } + config := config.ReadConfigs() + if config.CommonController.ControlPlane.Enabled { + go func() { + var controlPlane controlplane.ArtifactDeployer + if config.CommonController.ControlPlane.Persistence.Type == "K8s" { + controlPlane = controlplane.NewK8sArtifactDeployer(mgr) + + } + grpcClient := controlplane.NewControlPlaneAgent(config.CommonController.ControlPlane.Host, config.CommonController.ControlPlane.EventPort, controlPlaneID, controlPlane) + if grpcClient != nil { + grpcClient.StartEventStreaming() + } + }() + } setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { diff --git a/common-controller/internal/server/application_types.go b/common-controller/internal/server/application_types.go index 01ad4613b..e9e7f0bb9 100644 --- a/common-controller/internal/server/application_types.go +++ b/common-controller/internal/server/application_types.go @@ -24,6 +24,7 @@ type Application struct { Owner string `json:"owner"` Attributes map[string]string `json:"attributes,omitempty"` OrganizationID string `json:"organizationId"` + TimeStamp int64 `json:"timeStamp"` } // ApplicationList contains a list of Application diff --git a/common-controller/internal/utils/event_utils.go b/common-controller/internal/utils/event_utils.go index de350566d..d4c916224 100644 --- a/common-controller/internal/utils/event_utils.go +++ b/common-controller/internal/utils/event_utils.go @@ -48,8 +48,12 @@ func SendAppUpdateEvent(applicationUUID string, oldApplicationSpec cpv1alpha2.Ap } loggers.LoggerAPKOperator.Debugf("Sending event to all clients: %v", &event) sendEvent(&event) - sendDeleteApplicationKeyMappingEvent(applicationUUID, oldApplicationSpec) - sendApplicationKeyMappingEvent(applicationUUID, newApplicationSpec) + if oldApplicationSpec.SecuritySchemes != nil { + sendDeleteApplicationKeyMappingEvent(applicationUUID, oldApplicationSpec) + } + if newApplicationSpec.SecuritySchemes != nil { + sendApplicationKeyMappingEvent(applicationUUID, newApplicationSpec) + } } // SendAddApplicationEvent sends an application creation event to the enforcer @@ -69,7 +73,9 @@ func SendAddApplicationEvent(application cpv1alpha2.Application) { }, } sendEvent(&event) - sendApplicationKeyMappingEvent(application.ObjectMeta.Name, application.Spec) + if application.Spec.SecuritySchemes != nil { + sendApplicationKeyMappingEvent(application.ObjectMeta.Name, application.Spec) + } } // SendAddSubscriptionEvent sends an subscription creation event to the enforcer diff --git a/common-go-libs/apis/cp/v1alpha2/application_types.go b/common-go-libs/apis/cp/v1alpha2/application_types.go index e1110737c..e5aa40467 100644 --- a/common-go-libs/apis/cp/v1alpha2/application_types.go +++ b/common-go-libs/apis/cp/v1alpha2/application_types.go @@ -26,11 +26,14 @@ import ( // ApplicationSpec defines the desired state of Application type ApplicationSpec struct { - Name string `json:"name"` - Owner string `json:"owner"` - Organization string `json:"organization"` - Attributes map[string]string `json:"attributes,omitempty"` - SecuritySchemes SecuritySchemes `json:"securitySchemes"` + Name string `json:"name"` + Owner string `json:"owner"` + Organization string `json:"organization"` + // +optional + Attributes map[string]string `json:"attributes,omitempty"` + // +optional + + SecuritySchemes *SecuritySchemes `json:"securitySchemes"` } // SecuritySchemes defines the supported security schemes diff --git a/common-go-libs/apis/cp/v1alpha2/zz_generated.deepcopy.go b/common-go-libs/apis/cp/v1alpha2/zz_generated.deepcopy.go index 14dea1761..4d3fd79aa 100644 --- a/common-go-libs/apis/cp/v1alpha2/zz_generated.deepcopy.go +++ b/common-go-libs/apis/cp/v1alpha2/zz_generated.deepcopy.go @@ -199,7 +199,11 @@ func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) { (*out)[key] = val } } - in.SecuritySchemes.DeepCopyInto(&out.SecuritySchemes) + if in.SecuritySchemes != nil { + in, out := &in.SecuritySchemes, &out.SecuritySchemes + *out = new(SecuritySchemes) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSpec. diff --git a/common-go-libs/config/crd/bases/cp.wso2.com_applications.yaml b/common-go-libs/config/crd/bases/cp.wso2.com_applications.yaml index 63c1772c9..93f41b2cb 100644 --- a/common-go-libs/config/crd/bases/cp.wso2.com_applications.yaml +++ b/common-go-libs/config/crd/bases/cp.wso2.com_applications.yaml @@ -73,7 +73,6 @@ spec: - name - organization - owner - - securitySchemes type: object status: description: ApplicationStatus defines the observed state of Application diff --git a/helm-charts/crds/cp.wso2.com_applications.yaml b/helm-charts/crds/cp.wso2.com_applications.yaml index 63c1772c9..93f41b2cb 100644 --- a/helm-charts/crds/cp.wso2.com_applications.yaml +++ b/helm-charts/crds/cp.wso2.com_applications.yaml @@ -73,7 +73,6 @@ spec: - name - organization - owner - - securitySchemes type: object status: description: ApplicationStatus defines the observed state of Application diff --git a/helm-charts/templates/data-plane/gateway-components/common-log-conf.yaml b/helm-charts/templates/data-plane/gateway-components/common-log-conf.yaml index 3d85d7ef1..98563eb4b 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-log-conf.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-log-conf.yaml @@ -51,7 +51,9 @@ data: [commoncontroller.webServer] port = 9543 - + # [commoncontroller.controlplane] + # host= "apim-apk-agent-service.apk-agent.svc.cluster.local" + # port= 18000 log_config.toml: | # The logging configuration for Adapter diff --git a/helm-charts/values.yaml b/helm-charts/values.yaml index 3f1126941..f1bba48a8 100644 --- a/helm-charts/values.yaml +++ b/helm-charts/values.yaml @@ -144,7 +144,7 @@ wso2: strategy: RollingUpdate replicas: 1 imagePullPolicy: Always - image: wso2/apk-common-controller:1.1.0-m2 + image: wso2/apk-common-controller:latest security: sslHostname: "commoncontroller" # configs: