diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7c48126..edbc0fd 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: 1.22 cache: true - name: Build @@ -27,4 +27,4 @@ jobs: env: PORT_CLIENT_ID: ${{ secrets.PORT_CLIENT_ID }} PORT_CLIENT_SECRET: ${{ secrets.PORT_CLIENT_SECRET }} - + PORT_BASE_URL: https://api.stg-01.getport.io diff --git a/.gitignore b/.gitignore index 790622b..082f59d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,9 @@ config.yaml deployments/k8s -.env \ No newline at end of file +.env + +.vscode + +# debug file +__debug_bin* \ No newline at end of file diff --git a/go.mod b/go.mod index 9186364..68610e6 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/port-labs/port-k8s-exporter -go 1.21 +go 1.22.0 + +toolchain go1.22.2 require ( github.com/confluentinc/confluent-kafka-go/v2 v2.2.0 @@ -8,44 +10,45 @@ require ( github.com/google/uuid v1.3.0 github.com/itchyny/gojq v0.12.9 github.com/joho/godotenv v1.5.1 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 + golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.25.2 - k8s.io/apimachinery v0.25.2 - k8s.io/client-go v0.25.2 - k8s.io/klog/v2 v2.80.1 - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed + k8s.io/api v0.30.0 + k8s.io/apiextensions-apiserver v0.30.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 + k8s.io/klog/v2 v2.120.1 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b ) require ( - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/itchyny/timefmt-go v0.1.4 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.6.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -53,8 +56,8 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index d110fc2..64a5330 100644 --- a/go.sum +++ b/go.sum @@ -636,10 +636,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/actgardner/gogen-avro/v10 v10.2.1/go.mod h1:QUhjeHPchheYmMDni/Nx7VB0RsT/ee8YIgGY/xpEQgQ= @@ -687,8 +685,9 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= @@ -900,8 +899,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.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= @@ -962,27 +961,30 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= @@ -991,6 +993,8 @@ github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSM github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +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.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -1043,8 +1047,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -1052,8 +1057,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -1069,8 +1074,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -1096,6 +1102,7 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -1235,8 +1242,9 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB 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= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -1254,8 +1262,9 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -1343,8 +1352,8 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1356,8 +1365,8 @@ github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDs github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1454,8 +1463,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= @@ -1478,8 +1488,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1524,8 +1535,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1657,6 +1669,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1805,8 +1819,9 @@ golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2074,6 +2089,8 @@ golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2291,8 +2308,10 @@ google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 h1:0BOZf6qNozI3pkN3fJLwNubheHJYHhMh91GRFOWWK08= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -2336,8 +2355,9 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +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/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2417,15 +2437,17 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= -k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= -k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= @@ -2434,8 +2456,8 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= -k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= @@ -2454,20 +2476,20 @@ k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= @@ -2509,14 +2531,15 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/main.go b/main.go index b45f2fe..2f6469b 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "github.com/port-labs/port-k8s-exporter/pkg/config" "github.com/port-labs/port-k8s-exporter/pkg/defaults" "github.com/port-labs/port-k8s-exporter/pkg/event_handler" diff --git a/pkg/config/config.go b/pkg/config/config.go index 625cff3..4fc9554 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,12 +3,13 @@ package config import ( "flag" "fmt" + "os" + "strings" + "github.com/joho/godotenv" "github.com/port-labs/port-k8s-exporter/pkg/port" "gopkg.in/yaml.v2" "k8s.io/klog/v2" - "os" - "strings" ) var KafkaConfig = &KafkaConfiguration{} diff --git a/pkg/config/utils.go b/pkg/config/utils.go index 40c7d06..c5bab62 100644 --- a/pkg/config/utils.go +++ b/pkg/config/utils.go @@ -2,9 +2,10 @@ package config import ( "flag" + "strings" + "github.com/port-labs/port-k8s-exporter/pkg/goutils" "k8s.io/utils/strings/slices" - "strings" ) var keys []string diff --git a/pkg/crd/crd.go b/pkg/crd/crd.go new file mode 100644 index 0000000..296946b --- /dev/null +++ b/pkg/crd/crd.go @@ -0,0 +1,367 @@ +package crd + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/port-labs/port-k8s-exporter/pkg/goutils" + "github.com/port-labs/port-k8s-exporter/pkg/jq" + "github.com/port-labs/port-k8s-exporter/pkg/port" + "github.com/port-labs/port-k8s-exporter/pkg/port/blueprint" + "github.com/port-labs/port-k8s-exporter/pkg/port/cli" + "golang.org/x/exp/slices" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog/v2" +) + +const ( + KindCRD = "CustomResourceDefinition" + K8SIcon = "Cluster" + CrossplaneIcon = "Crossplane" +) + +var invisibleFields = []string{ + "writeConnectionSecretToRef", + "publishConnectionDetailsTo", + "resourceRefs", + "environmentConfigRefs", + "compositeDeletePolicy", + "resourceRef", + "claimRefs", + "compositionUpdatePolicy", + "compositionRevisionSelector", + "compositionRevisionRef", + "compositionSelector", + "compositionRef", + "claimRef", +} + +func createKindConfigFromCRD(crd v1.CustomResourceDefinition) port.Resource { + resource := crd.Spec.Names.Kind + group := crd.Spec.Group + version := crd.Spec.Versions[0].Name + kindConfig := port.Resource{ + Kind: group + "/" + version + "/" + resource, + Selector: port.Selector{ + Query: "true", + }, + Port: port.Port{ + Entity: port.EntityMappings{ + Mappings: []port.EntityMapping{ + { + Identifier: ".metadata.name", + Blueprint: "\"" + crd.Spec.Names.Singular + "\"", // Blueprint is a JQ query, so that way we hardcoded it + Title: ".metadata.name", + Properties: map[string]string{ + "namespace": ".metadata.namespace", + "*": ".spec", + }, + }, + }, + }, + }, + } + return kindConfig +} + +func isCRDNamespacedScoped(crd v1.CustomResourceDefinition) bool { + return crd.Spec.Scope == v1.NamespaceScoped +} + +func getDescriptionFromCRD(crd v1.CustomResourceDefinition) string { + return fmt.Sprintf("This action automatically generated from a Custom Resource Definition (CRD) in the cluster. Allows you to create, update, and delete %s resources. To complete the setup of this action, follow [this guide](https://docs.getport.io/guides-and-tutorials/manage-resources-using-k8s-crds)", crd.Spec.Names.Singular) +} +func getIconFromCRD(crd v1.CustomResourceDefinition) string { + if len(crd.ObjectMeta.OwnerReferences) > 0 && crd.ObjectMeta.OwnerReferences[0].Kind == "CompositeResourceDefinition" { + return CrossplaneIcon + } + return K8SIcon +} + +func buildCreateAction(crd v1.CustomResourceDefinition, as *port.ActionUserInputs, apiVersionProperty port.ActionProperty, kindProperty port.ActionProperty, nameProperty port.ActionProperty, namespaceProperty port.ActionProperty, invocation port.InvocationMethod) port.Action { + createActionProperties := goutils.MergeMaps( + as.Properties, + map[string]port.ActionProperty{"apiVersion": apiVersionProperty, "kind": kindProperty, "name": nameProperty}, + ) + + crtAct := port.Action{ + Identifier: "create_" + crd.Spec.Names.Singular, + Title: "Create " + strings.Title(crd.Spec.Names.Singular), + Icon: getIconFromCRD(crd), + UserInputs: port.ActionUserInputs{ + Properties: createActionProperties, + Required: append(as.Required, "name"), + }, + Description: getDescriptionFromCRD(crd), + Trigger: "CREATE", + InvocationMethod: &invocation, + } + + if isCRDNamespacedScoped(crd) { + crtAct.UserInputs.Properties["namespace"] = namespaceProperty + crtAct.UserInputs.Required = append(crtAct.UserInputs.Required, "namespace") + } + + return crtAct +} + +func buildUpdateAction(crd v1.CustomResourceDefinition, as *port.ActionUserInputs, apiVersionProperty port.ActionProperty, kindProperty port.ActionProperty, namespaceProperty port.ActionProperty, invocation port.InvocationMethod) port.Action { + if isCRDNamespacedScoped(crd) { + as.Properties["namespace"] = namespaceProperty + as.Required = append(as.Required, "namespace") + } + + for k, v := range as.Properties { + updatedStruct := v + + defaultMap := make(map[string]string) + defaultMap["jqQuery"] = ".entity.properties." + k + updatedStruct.Default = defaultMap + + as.Properties[k] = updatedStruct + } + + updateProperties := goutils.MergeMaps( + as.Properties, + map[string]port.ActionProperty{"apiVersion": apiVersionProperty, "kind": kindProperty}, + ) + + updtAct := port.Action{ + Identifier: "update_" + crd.Spec.Names.Singular, + Title: "Update " + strings.Title(crd.Spec.Names.Singular), + Icon: getIconFromCRD(crd), + Description: getDescriptionFromCRD(crd), + UserInputs: port.ActionUserInputs{ + Properties: updateProperties, + Required: as.Required, + }, + Trigger: "DAY-2", + InvocationMethod: &invocation, + } + + return updtAct +} + +func buildDeleteAction(crd v1.CustomResourceDefinition, apiVersionProperty port.ActionProperty, kindProperty port.ActionProperty, namespaceProperty port.ActionProperty, invocation port.InvocationMethod) port.Action { + dltAct := port.Action{ + Identifier: "delete_" + crd.Spec.Names.Singular, + Title: "Delete " + strings.Title(crd.Spec.Names.Singular), + Icon: getIconFromCRD(crd), + Description: getDescriptionFromCRD(crd), + Trigger: "DELETE", + UserInputs: port.ActionUserInputs{ + Properties: map[string]port.ActionProperty{ + "apiVersion": apiVersionProperty, + "kind": kindProperty, + }, + }, + InvocationMethod: &invocation, + } + + if isCRDNamespacedScoped(crd) { + visible := new(bool) // Using a pointer to bool to avoid the omitempty of false values + *visible = false + namespaceProperty.Visible = visible + dltAct.UserInputs.Properties["namespace"] = namespaceProperty + dltAct.UserInputs.Required = append(dltAct.UserInputs.Required, "namespace") + } + + return dltAct +} + +func convertToPortSchema(crd v1.CustomResourceDefinition) ([]port.Action, *port.Blueprint, error) { + latestCRDVersion := crd.Spec.Versions[0] + bs := &port.Schema{} + as := &port.ActionUserInputs{} + notVisible := new(bool) // Using a pointer to bool to avoid the omitempty of false values + *notVisible = false + + var spec v1.JSONSchemaProps + + // If the CRD has a spec field, use that as the schema - as it's a best practice but not required by k8s + if _, ok := latestCRDVersion.Schema.OpenAPIV3Schema.Properties["spec"]; ok { + spec = latestCRDVersion.Schema.OpenAPIV3Schema.Properties["spec"] + } else { + spec = *latestCRDVersion.Schema.OpenAPIV3Schema + } + + // Convert integer types to number as Port does not yet support integers + for i, v := range spec.Properties { + if v.Type == "integer" { + v.Type = "number" + v.Format = "" + spec.Properties[i] = v + } + } + + bytes, err := json.Marshal(&spec) + + if err != nil { + return nil, nil, fmt.Errorf("error marshaling schema: %v", err) + } + + err = json.Unmarshal(bytes, &bs) + if err != nil { + return nil, nil, fmt.Errorf("error unmarshaling schema into blueprint schema: %v", err) + } + + err = json.Unmarshal(bytes, &as) + if err != nil { + return nil, nil, fmt.Errorf("error unmarshaling schema into action schema: %v", err) + } + + if isCRDNamespacedScoped(crd) { + bs.Properties["namespace"] = port.Property{ + Type: "string", + Title: "Namespace", + } + } + + bp := port.Blueprint{ + Identifier: crd.Spec.Names.Singular, + Title: strings.Title(crd.Spec.Names.Singular), + Icon: getIconFromCRD(crd), + Schema: *bs, + } + + // Hide fields that are not commonly needed by default, with this approach we can still let the platform engineer show them afterwards if needed + for k, v := range as.Properties { + if slices.Contains(invisibleFields, k) { + v.Visible = notVisible + as.Properties[k] = v + } + } + + apiVersionProperty := port.ActionProperty{ + Type: "string", + Visible: notVisible, + Default: crd.Spec.Group + "/" + crd.Spec.Versions[0].Name, + } + + kindProperty := port.ActionProperty{ + Type: "string", + Visible: notVisible, + Default: crd.Spec.Names.Kind, + } + + nameProperty := port.ActionProperty{ + Type: "string", + Title: crd.Spec.Names.Singular + " Name", + Pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$", + } + + namespaceProperty := port.ActionProperty{ + Type: "string", + Title: "Namespace", + Default: "default", + } + + invocation := port.InvocationMethod{ + Type: "GITHUB", + Organization: "", + Repository: "", + Workflow: "sync-control-plane-direct.yml", + OmitPayload: false, + OmitUserInputs: true, + ReportWorkflowStatus: true, + } + + actions := []port.Action{ + buildCreateAction(crd, as, apiVersionProperty, kindProperty, nameProperty, namespaceProperty, invocation), + buildUpdateAction(crd, as, apiVersionProperty, kindProperty, namespaceProperty, invocation), + buildDeleteAction(crd, apiVersionProperty, kindProperty, namespaceProperty, invocation), + } + + return actions, &bp, nil +} + +func findMatchingCRDs(crds []v1.CustomResourceDefinition, pattern string) []v1.CustomResourceDefinition { + matchedCRDs := make([]v1.CustomResourceDefinition, 0) + + for _, crd := range crds { + mapCrd, err := goutils.StructToMap(crd) + + if err != nil { + klog.Errorf("Error converting CRD to map: %s", err.Error()) + continue + } + + match, err := jq.ParseBool(pattern, mapCrd) + + if err != nil { + klog.Errorf("Error running jq on crd CRD: %s", err.Error()) + continue + } + if match { + matchedCRDs = append(matchedCRDs, crd) + } + } + + return matchedCRDs +} + +func handleCRD(crds []v1.CustomResourceDefinition, portConfig *port.IntegrationAppConfig, portClient *cli.PortClient) { + matchedCRDs := findMatchingCRDs(crds, portConfig.CRDSToDiscover) + + for _, crd := range matchedCRDs { + portConfig.Resources = append(portConfig.Resources, createKindConfigFromCRD(crd)) + actions, bp, err := convertToPortSchema(crd) + if err != nil { + klog.Errorf("Error converting CRD to Port schemas: %s", err.Error()) + continue + } + + _, err = blueprint.NewBlueprint(portClient, *bp) + + if err != nil && strings.Contains(err.Error(), "taken") { + klog.Infof("Blueprint already exists, patching blueprint properties") + _, err = blueprint.PatchBlueprint(portClient, port.Blueprint{Schema: bp.Schema, Identifier: bp.Identifier}) + if err != nil { + klog.Errorf("Error patching blueprint: %s", err.Error()) + } + } + + if err != nil { + klog.Errorf("Error creating blueprint: %s", err.Error()) + } + + for _, act := range actions { + _, err = blueprint.NewBlueprintAction(portClient, bp.Identifier, act) + if err != nil { + if strings.Contains(err.Error(), "taken") { + if portConfig.OverwriteCRDsActions == true { + _, err = blueprint.UpdateBlueprintAction(portClient, bp.Identifier, act) + if err != nil { + klog.Errorf("Error updating blueprint action: %s", err.Error()) + } + } else { + klog.Infof("Action already exists, if you wish to overwrite it, delete it first or provide the configuration overwriteCrdsActions: true, in the exporter configuration and resync") + } + } else { + klog.Errorf("Error creating blueprint action: %s", err.Error()) + } + } + } + } +} + +func AutodiscoverCRDsToActions(portConfig *port.IntegrationAppConfig, apiExtensionsClient apiextensions.ApiextensionsV1Interface, portClient *cli.PortClient) { + if portConfig.CRDSToDiscover == "" { + klog.Info("Discovering CRDs is disabled") + return + } + + klog.Infof("Discovering CRDs/XRDs with pattern: %s", portConfig.CRDSToDiscover) + crds, err := apiExtensionsClient.CustomResourceDefinitions().List(context.Background(), metav1.ListOptions{}) + + if err != nil { + klog.Errorf("Error listing CRDs: %s", err.Error()) + return + } + + handleCRD(crds.Items, portConfig, portClient) +} diff --git a/pkg/crd/crd_test.go b/pkg/crd/crd_test.go new file mode 100644 index 0000000..1db8b85 --- /dev/null +++ b/pkg/crd/crd_test.go @@ -0,0 +1,259 @@ +package crd + +import ( + "testing" + + "github.com/port-labs/port-k8s-exporter/pkg/config" + "github.com/port-labs/port-k8s-exporter/pkg/port" + "github.com/port-labs/port-k8s-exporter/pkg/port/blueprint" + "github.com/port-labs/port-k8s-exporter/pkg/port/cli" + testUtils "github.com/port-labs/port-k8s-exporter/test_utils" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + fakeapiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/fake" + "k8s.io/apimachinery/pkg/runtime" + clienttesting "k8s.io/client-go/testing" +) + +type Fixture struct { + t *testing.T + apiextensionClient *fakeapiextensionsv1.FakeApiextensionsV1 + portClient *cli.PortClient + portConfig *port.IntegrationAppConfig +} + +func deleteDefaultResources(portClient *cli.PortClient) { + _ = blueprint.DeleteBlueprint(portClient, "testkind") +} + +func newFixture(t *testing.T, portClientId string, portClientSecret string, userAgent string, namespaced bool, crdsDiscoveryPattern string) *Fixture { + apiExtensionsFakeClient := fakeapiextensionsv1.FakeApiextensionsV1{Fake: &clienttesting.Fake{}} + + apiExtensionsFakeClient.AddReactor("list", "customresourcedefinitions", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { + fakeCrd := &v1.CustomResourceDefinitionList{ + Items: []v1.CustomResourceDefinition{ + { + Spec: v1.CustomResourceDefinitionSpec{ + Group: "testgroup", + Names: v1.CustomResourceDefinitionNames{ + Kind: "TestKind", + Singular: "testkind", + Plural: "testkinds", + }, + Versions: []v1.CustomResourceDefinitionVersion{ + { + Name: "v1", + Schema: &v1.CustomResourceValidation{ + OpenAPIV3Schema: &v1.JSONSchemaProps{ + Type: "object", + Properties: map[string]v1.JSONSchemaProps{ + "spec": { + Type: "object", + Properties: map[string]v1.JSONSchemaProps{ + "stringProperty": { + Type: "string", + }, + "intProperty": { + Type: "integer", + }, + "boolProperty": { + Type: "boolean", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + if namespaced { + fakeCrd.Items[0].Spec.Scope = v1.NamespaceScoped + } else { + fakeCrd.Items[0].Spec.Scope = v1.ClusterScoped + } + + return true, fakeCrd, nil + }) + + if portClientId == "" { + portClientId = config.ApplicationConfig.PortClientId + } + if portClientSecret == "" { + portClientSecret = config.ApplicationConfig.PortClientSecret + } + if userAgent == "" { + userAgent = "port-k8s-exporter/0.1" + } + + portClient, err := cli.New(config.ApplicationConfig.PortBaseURL, cli.WithHeader("User-Agent", userAgent), + cli.WithClientID(portClientId), cli.WithClientSecret(portClientSecret)) + deleteDefaultResources(portClient) + if err != nil { + t.Errorf("Error building Port client: %s", err.Error()) + } + + return &Fixture{ + t: t, + portClient: portClient, + apiextensionClient: &apiExtensionsFakeClient, + portConfig: &port.IntegrationAppConfig{ + CRDSToDiscover: crdsDiscoveryPattern, + }, + } +} + +func checkBlueprintAndActionsProperties(t *testing.T, f *Fixture, namespaced bool) { + + bp, err := blueprint.GetBlueprint(f.portClient, "testkind") + if err != nil { + t.Errorf("Error getting blueprint: %s", err.Error()) + } + t.Run("Check blueprint", func(t *testing.T) { + if bp == nil { + t.Errorf("Blueprint not found") + } + if bp.Schema.Properties["stringProperty"].Type != "string" { + t.Errorf("stringProperty type is not string") + } + if bp.Schema.Properties["intProperty"].Type != "number" { + t.Errorf("intProperty type is not number") + } + if bp.Schema.Properties["boolProperty"].Type != "boolean" { + t.Errorf("boolProperty type is not boolean") + } + if namespaced { + if bp.Schema.Properties["namespace"].Type != "string" { + t.Errorf("namespace type is not string") + } + } else { + if _, ok := bp.Schema.Properties["namespace"]; ok { + t.Errorf("namespace should not be present") + } + } + }) + + createAction, err := cli.GetAction(f.portClient, "testkind", "create_testkind") + if err != nil { + t.Errorf("Error getting create action: %s", err.Error()) + } + t.Run("Check create action", func(t *testing.T) { + if createAction == nil { + t.Errorf("Create action not found") + } + if *createAction.UserInputs.Properties["apiVersion"].Visible != false { + t.Errorf("apiVersion should not be visible") + } + if *createAction.UserInputs.Properties["kind"].Visible != false { + t.Errorf("kind should not be visible") + } + if createAction.UserInputs.Properties["stringProperty"].Type != "string" { + t.Errorf("stringProperty type is not string") + } + if createAction.UserInputs.Properties["intProperty"].Type != "number" { + t.Errorf("intProperty type is not number") + } + if createAction.UserInputs.Properties["boolProperty"].Type != "boolean" { + t.Errorf("boolProperty type is not boolean") + } + if namespaced { + if createAction.UserInputs.Properties["namespace"].Type != "string" { + t.Errorf("namespace type is not string") + } + } else { + if _, ok := createAction.UserInputs.Properties["namespace"]; ok { + t.Errorf("namespace should not be present") + } + } + }) + + updateAction, err := cli.GetAction(f.portClient, "testkind", "update_testkind") + if err != nil { + t.Errorf("Error getting update action: %s", err.Error()) + } + t.Run("Check update action", func(t *testing.T) { + if updateAction == nil { + t.Errorf("Update action not found") + } + if *updateAction.UserInputs.Properties["apiVersion"].Visible != false { + t.Errorf("apiVersion should not be visible") + } + if *updateAction.UserInputs.Properties["kind"].Visible != false { + t.Errorf("kind should not be visible") + } + if updateAction.UserInputs.Properties["stringProperty"].Type != "string" { + t.Errorf("stringProperty type is not string") + } + if updateAction.UserInputs.Properties["intProperty"].Type != "number" { + t.Errorf("intProperty type is not number") + } + if updateAction.UserInputs.Properties["boolProperty"].Type != "boolean" { + t.Errorf("boolProperty type is not boolean") + } + if namespaced { + if updateAction.UserInputs.Properties["namespace"].Type != "string" { + t.Errorf("namespace type is not string") + } + } else { + if _, ok := updateAction.UserInputs.Properties["namespace"]; ok { + t.Errorf("namespace should not be present") + } + } + }) + + deleteAction, err := cli.GetAction(f.portClient, "testkind", "delete_testkind") + if err != nil { + t.Errorf("Error getting delete action: %s", err.Error()) + } + t.Run("Check delete action", func(t *testing.T) { + if deleteAction == nil { + t.Errorf("Delete action not found") + } + if *deleteAction.UserInputs.Properties["apiVersion"].Visible != false { + t.Errorf("apiVersion should not be visible") + } + if *deleteAction.UserInputs.Properties["kind"].Visible != false { + t.Errorf("kind should not be visible") + } + if namespaced { + if deleteAction.UserInputs.Properties["namespace"].Type != "string" { + t.Errorf("namespace type is not string") + } + } else { + if _, ok := deleteAction.UserInputs.Properties["namespace"]; ok { + t.Errorf("namespace should not be present") + } + } + }) +} + +func TestCRD_crd_autoDiscoverCRDsToActionsClusterScoped(t *testing.T) { + f := newFixture(t, "", "", "", false, "true") + + AutodiscoverCRDsToActions(f.portConfig, f.apiextensionClient, f.portClient) + + checkBlueprintAndActionsProperties(t, f, false) + + testUtils.CheckResourcesExistence(true, f.portClient, t, []string{"testkind"}, []string{}) +} + +func TestCRD_crd_autoDiscoverCRDsToActionsNamespaced(t *testing.T) { + f := newFixture(t, "", "", "", true, "true") + + AutodiscoverCRDsToActions(f.portConfig, f.apiextensionClient, f.portClient) + + checkBlueprintAndActionsProperties(t, f, true) + + testUtils.CheckResourcesExistence(true, f.portClient, t, []string{"testkind"}, []string{}) +} + +func TestCRD_crd_autoDiscoverCRDsToActionsNoCRDs(t *testing.T) { + f := newFixture(t, "", "", "", false, "false") + + AutodiscoverCRDsToActions(f.portConfig, f.apiextensionClient, f.portClient) + + testUtils.CheckResourcesExistence(false, f.portClient, t, []string{"testkind"}, []string{}) +} diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go index c179d4e..87ceffb 100644 --- a/pkg/defaults/defaults.go +++ b/pkg/defaults/defaults.go @@ -3,6 +3,9 @@ package defaults import ( "encoding/json" "fmt" + "os" + "sync" + "github.com/port-labs/port-k8s-exporter/pkg/port" "github.com/port-labs/port-k8s-exporter/pkg/port/blueprint" "github.com/port-labs/port-k8s-exporter/pkg/port/cli" @@ -11,8 +14,6 @@ import ( "github.com/port-labs/port-k8s-exporter/pkg/port/scorecards" "gopkg.in/yaml.v3" "k8s.io/klog/v2" - "os" - "sync" ) type ScorecardDefault struct { diff --git a/pkg/defaults/defaults_test.go b/pkg/defaults/defaults_test.go index dc75d16..c262fee 100644 --- a/pkg/defaults/defaults_test.go +++ b/pkg/defaults/defaults_test.go @@ -2,6 +2,8 @@ package defaults import ( "fmt" + "testing" + guuid "github.com/google/uuid" "github.com/port-labs/port-k8s-exporter/pkg/config" "github.com/port-labs/port-k8s-exporter/pkg/port" @@ -9,9 +11,8 @@ import ( "github.com/port-labs/port-k8s-exporter/pkg/port/cli" "github.com/port-labs/port-k8s-exporter/pkg/port/integration" "github.com/port-labs/port-k8s-exporter/pkg/port/page" - _ "github.com/port-labs/port-k8s-exporter/test_utils" + testUtils "github.com/port-labs/port-k8s-exporter/test_utils" "github.com/stretchr/testify/assert" - "testing" ) type Fixture struct { @@ -20,24 +21,6 @@ type Fixture struct { stateKey string } -func checkResourcesDoesNotExist(f *Fixture, blueprints []string, pages []string) { - for _, bp := range blueprints { - _, err := blueprint.GetBlueprint(f.portClient, bp) - if err != nil { - _ = blueprint.DeleteBlueprint(f.portClient, bp) - } - assert.NotNil(f.t, err) - } - - for _, p := range pages { - _, err := page.GetPage(f.portClient, p) - if err != nil { - _ = page.DeletePage(f.portClient, p) - } - assert.NotNil(f.t, err) - } -} - func NewFixture(t *testing.T) *Fixture { stateKey := guuid.NewString() portClient, err := cli.New(config.ApplicationConfig.PortBaseURL, cli.WithHeader("User-Agent", fmt.Sprintf("port-k8s-exporter/0.1 (statekey/%s)", stateKey)), @@ -117,7 +100,7 @@ func Test_InitIntegration_InitDefaults_CreateDefaultResources_False(t *testing.T _, err := integration.GetIntegration(f.portClient, f.stateKey) assert.Nil(t, err) - checkResourcesDoesNotExist(f, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) } func Test_InitIntegration_BlueprintExists(t *testing.T) { @@ -125,8 +108,8 @@ func Test_InitIntegration_BlueprintExists(t *testing.T) { if _, err := blueprint.NewBlueprint(f.portClient, port.Blueprint{ Identifier: "workload", Title: "Workload", - Schema: port.BlueprintSchema{ - Properties: map[string]port.BlueprintProperty{}, + Schema: port.Schema{ + Properties: map[string]port.Property{}, }, }); err != nil { t.Errorf("Error creating Port blueprint: %s", err.Error()) @@ -145,7 +128,7 @@ func Test_InitIntegration_BlueprintExists(t *testing.T) { _, err = blueprint.GetBlueprint(f.portClient, "workload") assert.Nil(t, err) - checkResourcesDoesNotExist(f, []string{"namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) } func Test_InitIntegration_PageExists(t *testing.T) { @@ -170,7 +153,7 @@ func Test_InitIntegration_PageExists(t *testing.T) { _, err = page.GetPage(f.portClient, "workload_overview_dashboard") assert.Nil(t, err) - checkResourcesDoesNotExist(f, []string{"workload", "namespace", "cluster"}, []string{"availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"workload", "namespace", "cluster"}, []string{"availability_scorecard_dashboard"}) } func Test_InitIntegration_ExistingIntegration(t *testing.T) { @@ -189,7 +172,7 @@ func Test_InitIntegration_ExistingIntegration(t *testing.T) { _, err = integration.GetIntegration(f.portClient, f.stateKey) assert.Nil(t, err) - checkResourcesDoesNotExist(f, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) } func Test_InitIntegration_LocalResourcesConfiguration(t *testing.T) { @@ -230,7 +213,7 @@ func Test_InitIntegration_LocalResourcesConfiguration(t *testing.T) { assert.Equal(t, expectedResources, i.Config.Resources) assert.Nil(t, err) - checkResourcesDoesNotExist(f, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) } func Test_InitIntegration_LocalResourcesConfiguration_ExistingIntegration_EmptyConfiguration(t *testing.T) { @@ -251,7 +234,7 @@ func Test_InitIntegration_LocalResourcesConfiguration_ExistingIntegration_EmptyC assert.Nil(t, err) assert.Equal(t, "KAFKA", i.EventListener.Type) - checkResourcesDoesNotExist(f, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) } func Test_InitIntegration_LocalResourcesConfiguration_ExistingIntegration_WithConfiguration_WithOverwriteConfigurationOnRestartFlag(t *testing.T) { @@ -297,5 +280,5 @@ func Test_InitIntegration_LocalResourcesConfiguration_ExistingIntegration_WithCo assert.Nil(t, err) assert.Equal(t, expectedConfig.Resources, i.Config.Resources) - checkResourcesDoesNotExist(f, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) + testUtils.CheckResourcesExistence(false, f.portClient, f.t, []string{"workload", "namespace", "cluster"}, []string{"workload_overview_dashboard", "availability_scorecard_dashboard"}) } diff --git a/pkg/event_handler/consumer/consumer.go b/pkg/event_handler/consumer/consumer.go index 832ae8c..f9f08a1 100644 --- a/pkg/event_handler/consumer/consumer.go +++ b/pkg/event_handler/consumer/consumer.go @@ -1,12 +1,13 @@ package consumer import ( - "github.com/confluentinc/confluent-kafka-go/v2/kafka" - "github.com/port-labs/port-k8s-exporter/pkg/config" - "k8s.io/klog/v2" "os" "os/signal" "syscall" + + "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/port-labs/port-k8s-exporter/pkg/config" + "k8s.io/klog/v2" ) type IConsume interface { @@ -90,7 +91,7 @@ func (c *Consumer) Consume(topic string, handler JsonHandler, readyChan chan boo handler(e.Value) if _, err := c.client.Commit(); err != nil { - klog.Error("Error committing offset: %s", err.Error()) + klog.Errorf("Error committing offset: %s", err.Error()) } case kafka.Error: klog.Infof("%% Error: %v\n", e) diff --git a/pkg/event_handler/consumer/consumer_test.go b/pkg/event_handler/consumer/consumer_test.go index 64a392c..aaf1db3 100644 --- a/pkg/event_handler/consumer/consumer_test.go +++ b/pkg/event_handler/consumer/consumer_test.go @@ -1,10 +1,11 @@ package consumer import ( - "github.com/confluentinc/confluent-kafka-go/v2/kafka" "testing" "time" + "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/port-labs/port-k8s-exporter/pkg/config" ) diff --git a/pkg/goutils/map.go b/pkg/goutils/map.go index d6df31a..037cc68 100644 --- a/pkg/goutils/map.go +++ b/pkg/goutils/map.go @@ -1,7 +1,9 @@ package goutils -func MergeMaps(ms ...map[string]interface{}) map[string]interface{} { - res := map[string]interface{}{} +import "encoding/json" + +func MergeMaps[T interface{}](ms ...map[string]T) map[string]T { + res := map[string]T{} for _, m := range ms { for k, v := range m { res[k] = v @@ -9,3 +11,14 @@ func MergeMaps(ms ...map[string]interface{}) map[string]interface{} { } return res } + +func StructToMap(obj interface{}) (newMap map[string]interface{}, err error) { + data, err := json.Marshal(obj) + + if err != nil { + return + } + + err = json.Unmarshal(data, &newMap) + return +} diff --git a/pkg/handlers/controllers.go b/pkg/handlers/controllers.go index 4c14395..bb4da4b 100644 --- a/pkg/handlers/controllers.go +++ b/pkg/handlers/controllers.go @@ -2,6 +2,9 @@ package handlers import ( "context" + "time" + + "github.com/port-labs/port-k8s-exporter/pkg/crd" "github.com/port-labs/port-k8s-exporter/pkg/goutils" "github.com/port-labs/port-k8s-exporter/pkg/k8s" "github.com/port-labs/port-k8s-exporter/pkg/port" @@ -10,7 +13,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/klog/v2" - "time" ) type ControllersHandler struct { @@ -25,6 +27,8 @@ func NewControllersHandler(exporterConfig *port.Config, portConfig *port.Integra resync := time.Minute * time.Duration(exporterConfig.ResyncInterval) informersFactory := dynamicinformer.NewDynamicSharedInformerFactory(k8sClient.DynamicClient, resync) + crd.AutodiscoverCRDsToActions(portConfig, k8sClient.ApiExtensionClient, portClient) + aggResources := make(map[string][]port.KindConfig) for _, resource := range portConfig.Resources { kindConfig := port.KindConfig{Selector: resource.Selector, Port: resource.Port} diff --git a/pkg/jq/parser.go b/pkg/jq/parser.go index bd3e3ee..1d518f2 100644 --- a/pkg/jq/parser.go +++ b/pkg/jq/parser.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/itchyny/gojq" + "github.com/port-labs/port-k8s-exporter/pkg/goutils" "k8s.io/klog/v2" ) @@ -106,7 +107,12 @@ func ParseMapInterface(jqQueries map[string]string, obj interface{}) (map[string return nil, err } - mapInterface[key] = queryRes + if key != "*" { + mapInterface[key] = queryRes + } else { + mapInterface = goutils.MergeMaps(mapInterface, queryRes.(map[string]interface{})) + } + } return mapInterface, nil diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 95ef6d1..21857d2 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -1,6 +1,7 @@ package k8s import ( + apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/memory" "k8s.io/client-go/dynamic" @@ -9,9 +10,10 @@ import ( ) type Client struct { - DiscoveryClient *discovery.DiscoveryClient - DynamicClient dynamic.Interface - DiscoveryMapper *restmapper.DeferredDiscoveryRESTMapper + DiscoveryClient *discovery.DiscoveryClient + DynamicClient dynamic.Interface + DiscoveryMapper *restmapper.DeferredDiscoveryRESTMapper + ApiExtensionClient *apiextensions.ApiextensionsV1Client } func NewClient(config *rest.Config) (*Client, error) { @@ -26,10 +28,12 @@ func NewClient(config *rest.Config) (*Client, error) { return nil, err } + apiextensionsClient, err := apiextensions.NewForConfig(config) + cacheClient := memory.NewMemCacheClient(discoveryClient) cacheClient.Invalidate() discoveryMapper := restmapper.NewDeferredDiscoveryRESTMapper(cacheClient) - return &Client{discoveryClient, dynamicClient, discoveryMapper}, nil + return &Client{discoveryClient, dynamicClient, discoveryMapper, apiextensionsClient}, nil } diff --git a/pkg/k8s/resource.go b/pkg/k8s/resource.go index 05f499e..29dacb2 100644 --- a/pkg/k8s/resource.go +++ b/pkg/k8s/resource.go @@ -1,9 +1,10 @@ package k8s import ( + "strings" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/restmapper" - "strings" ) func GetGVRFromResource(discoveryMapper *restmapper.DeferredDiscoveryRESTMapper, resource string) (schema.GroupVersionResource, error) { diff --git a/pkg/port/blueprint/blueprint.go b/pkg/port/blueprint/blueprint.go index 6bb44ad..cf4042a 100644 --- a/pkg/port/blueprint/blueprint.go +++ b/pkg/port/blueprint/blueprint.go @@ -3,23 +3,49 @@ package blueprint import ( "context" "fmt" + "github.com/port-labs/port-k8s-exporter/pkg/port" "github.com/port-labs/port-k8s-exporter/pkg/port/cli" ) func NewBlueprint(portClient *cli.PortClient, blueprint port.Blueprint) (*port.Blueprint, error) { _, err := portClient.Authenticate(context.Background(), portClient.ClientID, portClient.ClientSecret) + if err != nil { return nil, fmt.Errorf("error authenticating with Port: %v", err) } bp, err := cli.CreateBlueprint(portClient, blueprint) if err != nil { - return nil, fmt.Errorf("error creating Port blueprint: %v", err) + return nil, fmt.Errorf("error creating blueprint: %v", err) } return bp, nil } +func NewBlueprintAction(portClient *cli.PortClient, blueprintIdentifier string, action port.Action) (*port.Action, error) { + _, err := portClient.Authenticate(context.Background(), portClient.ClientID, portClient.ClientSecret) + if err != nil { + return nil, fmt.Errorf("error authenticating with Port: %v", err) + } + act, err := cli.CreateAction(portClient, blueprintIdentifier, action) + if err != nil { + return nil, fmt.Errorf("error creating blueprint action: %v", err) + } + return act, nil +} + +func UpdateBlueprintAction(portClient *cli.PortClient, blueprintIdentifier string, action port.Action) (*port.Action, error) { + _, err := portClient.Authenticate(context.Background(), portClient.ClientID, portClient.ClientSecret) + if err != nil { + return nil, fmt.Errorf("error authenticating with Port: %v", err) + } + act, err := cli.UpdateAction(portClient, blueprintIdentifier, action) + if err != nil { + return nil, fmt.Errorf("error updating blueprint action: %v", err) + } + return act, nil +} + func PatchBlueprint(portClient *cli.PortClient, blueprint port.Blueprint) (*port.Blueprint, error) { _, err := portClient.Authenticate(context.Background(), portClient.ClientID, portClient.ClientSecret) if err != nil { diff --git a/pkg/port/cli/action.go b/pkg/port/cli/action.go new file mode 100644 index 0000000..ddcf13e --- /dev/null +++ b/pkg/port/cli/action.go @@ -0,0 +1,51 @@ +package cli + +import ( + "fmt" + + "github.com/port-labs/port-k8s-exporter/pkg/port" +) + +func CreateAction(portClient *PortClient, blueprintIdentifier string, action port.Action) (*port.Action, error) { + pb := &port.ResponseBody{} + resp, err := portClient.Client.R(). + SetResult(&pb). + SetBody(action). + Post(fmt.Sprintf("v1/blueprints/%s/actions/", blueprintIdentifier)) + if err != nil { + return nil, err + } + if !pb.OK { + return nil, fmt.Errorf("failed to create action, got: %s", resp.Body()) + } + return &pb.Action, nil +} + +func UpdateAction(portClient *PortClient, blueprintIdentifier string, action port.Action) (*port.Action, error) { + pb := &port.ResponseBody{} + resp, err := portClient.Client.R(). + SetResult(&pb). + SetBody(action). + Put(fmt.Sprintf("v1/blueprints/%s/actions/%s", blueprintIdentifier, action.Identifier)) + if err != nil { + return nil, err + } + if !pb.OK { + return nil, fmt.Errorf("failed to patch action, got: %s", resp.Body()) + } + return &pb.Action, nil +} + +func GetAction(portClient *PortClient, blueprintIdentifier string, actionIdentifier string) (*port.Action, error) { + pb := &port.ResponseBody{} + resp, err := portClient.Client.R(). + SetResult(&pb). + Get(fmt.Sprintf("v1/blueprints/%s/actions/%s", blueprintIdentifier, actionIdentifier)) + if err != nil { + return nil, err + } + if !pb.OK { + return nil, fmt.Errorf("failed to get action, got: %s", resp.Body()) + } + return &pb.Action, nil +} diff --git a/pkg/port/cli/blueprint.go b/pkg/port/cli/blueprint.go index 571ae2d..4ca5fa8 100644 --- a/pkg/port/cli/blueprint.go +++ b/pkg/port/cli/blueprint.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "github.com/port-labs/port-k8s-exporter/pkg/port" ) diff --git a/pkg/port/models.go b/pkg/port/models.go index 722f7d9..32cbd5b 100644 --- a/pkg/port/models.go +++ b/pkg/port/models.go @@ -38,7 +38,7 @@ type ( UpdatedAt *time.Time `json:"updatedAt,omitempty"` } - BlueprintProperty struct { + Property struct { Type string `json:"type,omitempty"` Title string `json:"title,omitempty"` Identifier string `json:"identifier,omitempty"` @@ -52,6 +52,21 @@ type ( EnumColors map[string]string `json:"enumColors,omitempty"` } + ActionProperty struct { + Type string `json:"type,omitempty"` + Title string `json:"title,omitempty"` + Identifier string `json:"identifier,omitempty"` + Default any `json:"default,omitempty"` + Icon string `json:"icon,omitempty"` + Format string `json:"format,omitempty"` + Description string `json:"description,omitempty"` + Blueprint string `json:"blueprint,omitempty"` + Pattern string `json:"pattern,omitempty"` + Enum []string `json:"enum,omitempty"` + EnumColors map[string]string `json:"enumColors,omitempty"` + Visible *bool `json:"visible,omitempty"` + } + BlueprintMirrorProperty struct { Identifier string `json:"identifier,omitempty"` Title string `json:"title,omitempty"` @@ -78,14 +93,20 @@ type ( Type string `json:"type,omitempty"` } - BlueprintSchema struct { - Properties map[string]BlueprintProperty `json:"properties"` - Required []string `json:"required,omitempty"` + Schema struct { + Properties map[string]Property `json:"properties"` + Required []string `json:"required,omitempty"` } InvocationMethod struct { - Type string `json:"type,omitempty"` - Url string `json:"url,omitempty"` + Type string `json:"type,omitempty"` + Url string `json:"url,omitempty"` + Organization string `json:"org,omitempty"` + Repository string `json:"repo,omitempty"` + Workflow string `json:"workflow,omitempty"` + OmitUserInputs bool `json:"omitUserInputs,omitempty"` + OmitPayload bool `json:"omitPayload,omitempty"` + ReportWorkflowStatus bool `json:"reportWorkflowStatus,omitempty"` } ChangelogDestination struct { @@ -93,7 +114,10 @@ type ( Url string `json:"url,omitempty"` } - ActionUserInputs = BlueprintSchema + ActionUserInputs struct { + Properties map[string]ActionProperty `json:"properties"` + Required []string `json:"required,omitempty"` + } Blueprint struct { Meta @@ -101,7 +125,7 @@ type ( Title string `json:"title,omitempty"` Icon string `json:"icon"` Description string `json:"description"` - Schema BlueprintSchema `json:"schema"` + Schema Schema `json:"schema"` CalculationProperties map[string]BlueprintCalculationProperty `json:"calculationProperties,omitempty"` AggregationProperties map[string]BlueprintAggregationProperty `json:"aggregationProperties,omitempty"` MirrorProperties map[string]BlueprintMirrorProperty `json:"mirrorProperties,omitempty"` @@ -225,6 +249,8 @@ type IntegrationAppConfig struct { DeleteDependents bool `json:"deleteDependents,omitempty" yaml:"deleteDependents,omitempty"` CreateMissingRelatedEntities bool `json:"createMissingRelatedEntities,omitempty" yaml:"createMissingRelatedEntities,omitempty"` Resources []Resource `json:"resources,omitempty" yaml:"resources,omitempty"` + CRDSToDiscover string `json:"crdsToDiscover,omitempty"` + OverwriteCRDsActions bool `json:"overwriteCrdsActions,omitempty"` } type Config struct { @@ -235,6 +261,8 @@ type Config struct { OverwriteConfigurationOnRestart bool `yaml:"overwriteConfigurationOnRestart,omitempty"` // These Configurations are used only for setting up the Integration on installation or when using OverwriteConfigurationOnRestart flag. Resources []Resource `yaml:"resources,omitempty"` + CRDSToDiscover string `yaml:"crdsToDiscover,omitempty"` + OverwriteCRDsActions bool `yaml:"overwriteCrdsActions,omitempty"` DeleteDependents bool `yaml:"deleteDependents,omitempty"` CreateMissingRelatedEntities bool `yaml:"createMissingRelatedEntities,omitempty"` } diff --git a/test_utils/cleanup.go b/test_utils/cleanup.go new file mode 100644 index 0000000..39ff260 --- /dev/null +++ b/test_utils/cleanup.go @@ -0,0 +1,36 @@ +package testing_init + +import ( + "testing" + + "github.com/port-labs/port-k8s-exporter/pkg/port/blueprint" + "github.com/port-labs/port-k8s-exporter/pkg/port/cli" + "github.com/port-labs/port-k8s-exporter/pkg/port/page" + "github.com/stretchr/testify/assert" +) + +func CheckResourcesExistence(shouldExist bool, portClient *cli.PortClient, t *testing.T, blueprints []string, pages []string) { + for _, bp := range blueprints { + _, err := blueprint.GetBlueprint(portClient, bp) + if err == nil { + _ = blueprint.DeleteBlueprint(portClient, bp) + } + if shouldExist { + assert.Nil(t, err) + } else { + assert.NotNil(t, err) + } + } + + for _, p := range pages { + _, err := page.GetPage(portClient, p) + if err == nil { + _ = page.DeletePage(portClient, p) + } + if shouldExist { + assert.Nil(t, err) + } else { + assert.NotNil(t, err) + } + } +}