diff --git a/go.mod b/go.mod index ff39866..5aa9b34 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ toolchain go1.22.5 require ( cloud.google.com/go/container v1.40.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 github.com/alecthomas/kong v1.2.1 github.com/aws/aws-sdk-go v1.55.5 github.com/mattn/go-colorable v0.1.13 @@ -13,7 +15,6 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 google.golang.org/api v0.203.0 - google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 k8s.io/client-go v0.29.1 ) @@ -21,22 +22,29 @@ require ( cloud.google.com/go/auth v0.9.9 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.8 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/go.sum b/go.sum index fbb4373..07c3cd2 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,20 @@ cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixA cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/container v1.40.0 h1:JVoEg/4RvoGW37r2Eja/cTBc3X9c2loGWYq7QDsRDuI= cloud.google.com/go/container v1.40.0/go.mod h1:wNI1mOUivm+ZkpHMbouutgbD4sQxyphMwK31X5cThY4= -cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= -cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 h1:figxyQZXzZQIcP3njhC68bYUiTw45J8/SsHaLW8Ax0M= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0/go.mod h1:TmlMW4W5OvXOmOyKNnor8nlMMiO1ctIyzmHme/VHsrA= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= @@ -21,12 +33,16 @@ github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW5 github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 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= @@ -50,6 +66,8 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -101,8 +119,16 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -117,11 +143,17 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -189,6 +221,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= @@ -220,8 +253,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 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-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU= -google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= @@ -244,8 +275,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 57e94c7..c45bab4 100644 --- a/main.go +++ b/main.go @@ -6,13 +6,14 @@ import ( "github.com/alecthomas/kong" "github.com/clouddrove/kuconf/program/aws" + "github.com/clouddrove/kuconf/program/azure" "github.com/clouddrove/kuconf/program/gcp" "github.com/rs/zerolog/log" ) func main() { if len(os.Args) < 2 { - fmt.Println("Please provide (aws or gcp) as the first argument.") + fmt.Println("Please provide (aws, gcp or azure) as the first argument.") os.Exit(1) } @@ -20,6 +21,7 @@ func main() { var optionsGCP gcp.Options var optionsAWS aws.Options + var optionsAZURE azure.Options var ctx *kong.Context var err error @@ -48,9 +50,20 @@ func main() { log.Err(err).Msg("Program failed for AWS") os.Exit(1) } + + case "azure": + ctx, err = optionsAZURE.Parse(os.Args[2:]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if err := ctx.Run(&optionsAZURE); err != nil { + log.Err(err).Msg("Program failed for AZURE") + os.Exit(1) + } default: - fmt.Println("Invalid cloud provider. Please choose either 'aws' or 'gcp'.") + fmt.Println("Invalid cloud provider. Please choose either 'aws', 'gcp' or 'azure") os.Exit(1) } } diff --git a/program/aws/program_test.go b/program/aws/program_test.go deleted file mode 100644 index bef48b7..0000000 --- a/program/aws/program_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package aws - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestOptions_ReadConfig(t *testing.T) { - program := Options{KubeConfig: "testdata/config"} - - config, err := program.ReadConfig() - - assert.NoError(t, err) - assert.NotNil(t, config) - - assert.Equal(t, 6, len(config.Clusters)) - - c := config.Clusters["docker-desktop"] - assert.Equal(t, "https://kubernetes.docker.internal:6443", c.Server) -} \ No newline at end of file diff --git a/program/azure/azure.go b/program/azure/azure.go new file mode 100644 index 0000000..d6f7ce4 --- /dev/null +++ b/program/azure/azure.go @@ -0,0 +1,222 @@ +package azure + +import ( + "bufio" + "context" + "fmt" + "os" + "strings" + "sync" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +type azureSessionInfo struct { + subscription string + location string + client *armcontainerservice.ManagedClustersClient + log zerolog.Logger +} + +type AzureClusterInfo struct { + *armcontainerservice.ManagedCluster + log zerolog.Logger + session *azureSessionInfo +} + +func (program *Options) getSubscriptions() <-chan string { + output := make(chan string) + + if len(program.Subscriptions) < 1 { + go func() { + defer close(output) + if f, err := os.Open(program.SubscriptionFile); err == nil { + scanner := bufio.NewScanner(f) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + if s != "" { + output <- s + } + } + } else { + log.Error().Str("file", program.SubscriptionFile).Err(err).Msg("Failed to open subscription file") + } + }() + } else { + go func() { + defer close(output) + for _, s := range program.Subscriptions { + output <- s + } + }() + } + + return output +} + +func (program *Options) getClustersFrom(s *azureSessionInfo, clusters chan<- AzureClusterInfo) { + var wg sync.WaitGroup + defer wg.Wait() + + client := s.client + ctx := context.Background() + uniqueClusters := make(map[string]struct{}) + + pager := client.NewListPager(nil) + + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + s.log.Error().Err(err).Msg("Error listing AKS clusters") + return + } + + for _, c := range page.Value { + clusterKey := fmt.Sprintf("%s-%s", *c.Name, *c.Location) + if _, exists := uniqueClusters[clusterKey]; !exists { + uniqueClusters[clusterKey] = struct{}{} + stats.Clusters.Add(1) + + wg.Add(1) + go func(c *armcontainerservice.ManagedCluster) { + defer wg.Done() + + s.log.Debug(). + Str("cluster_name", *c.Name). + Str("location", *c.Location). + Msg("Found unique AKS cluster") + + clusters <- AzureClusterInfo{ + ManagedCluster: c, + log: s.log.With().Str("cluster_name", *c.Name).Str("location", *c.Location).Logger(), + session: s, + } + }(c) + } + } + } +} + +func (program *Options) getUniqueAzureSessions() <-chan *azureSessionInfo { + sessions := make(chan *azureSessionInfo) + + go func() { + wg := sync.WaitGroup{} + defer close(sessions) + defer wg.Wait() + + subscriptions := make(map[string]bool) + for info := range program.getSubscriptionSessions() { + if _, found := subscriptions[info.subscription]; found { + info.log.Debug().Msg("Subscription is duplicate") + continue + } + + stats.UniqueSubscriptions.Add(1) + info.log.Debug().Msg("Subscription is good for use") + subscriptions[info.subscription] = true + sessions <- info + + for _, location := range program.Locations { + if location != info.location { + stats.Locations.Add(1) + wg.Add(1) + go func(subscription, location string) { + defer wg.Done() + log := log.With().Str("subscription", subscription).Str("location", location).Logger() + log.Debug().Msg("Creating regional session") + + if s, err := program.newAzureSession(subscription, location); err == nil { + sessions <- s + } else { + log.Error().Err(err).Msg("Failed to create Azure session") + } + }(info.subscription, location) + } + } + } + }() + + return sessions +} + +func (program *Options) getSubscriptionSessions() <-chan *azureSessionInfo { + sessions := make(chan *azureSessionInfo) + wg := sync.WaitGroup{} + + go func() { + defer close(sessions) + defer wg.Wait() + + subscriptions := program.getSubscriptions() + + for s := range subscriptions { + stats.Subscriptions.Add(1) + log := log.With().Str("subscription", s).Str("location", program.Locations[0]).Logger() + wg.Add(1) + go func(s string) { + defer wg.Done() + if session, err := NewAzureSession(s, program.Locations[0], log); err == nil { + stats.UsableSubscriptions.Add(1) + sessions <- session + } + }(s) + } + }() + + return sessions +} + +func (program *Options) newAzureSession(subscription, location string) (*azureSessionInfo, error) { + cred, err := program.getAzureCredential() + if err != nil { + return nil, err + } + + client, err := armcontainerservice.NewManagedClustersClient(subscription, cred, nil) + if err != nil { + return nil, err + } + + logger := log.With().Str("subscription", subscription).Str("location", location).Logger() + logger.Debug().Msg("Azure subscription session created") + + return &azureSessionInfo{ + subscription: subscription, + location: location, + client: client, + log: logger, + }, nil +} + +func (program *Options) getAzureCredential() (*azidentity.DefaultAzureCredential, error) { + return azidentity.NewDefaultAzureCredential(nil) +} + +func NewAzureSession(subscription, location string, log zerolog.Logger) (*azureSessionInfo, error) { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + log.Error().Err(err).Msg("Failed to create Azure credential") + return nil, err + } + + client, err := armcontainerservice.NewManagedClustersClient(subscription, cred, nil) + if err != nil { + log.Error().Err(err).Msg("Failed to create Azure client factory") + return nil, err + } + + log.Debug().Msg("Azure subscription session created successfully") + + return &azureSessionInfo{ + subscription: subscription, + location: location, + client: client, + log: log, + }, nil +} \ No newline at end of file diff --git a/program/azure/config.go b/program/azure/config.go new file mode 100644 index 0000000..01ecbbf --- /dev/null +++ b/program/azure/config.go @@ -0,0 +1,92 @@ +package azure + +import ( + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" + "os" +) + +func captureConfig(c AzureClusterInfo, resourceGroup string, i *api.Config) error { + certificateData := []byte(*c.ManagedCluster.Properties.NetworkProfile.ServiceCidr) + + cluster := api.Cluster{ + Server: "https://" + *c.ManagedCluster.Properties.Fqdn, + CertificateAuthorityData: certificateData, + } + + user := api.AuthInfo{ + Exec: &api.ExecConfig{ + APIVersion: "client.authentication.k8s.io/v1beta1", + Command: "azure-cli", + Args: []string{ + "aks", "get-credentials", + "--resource-group", resourceGroup, + "--name", *c.ManagedCluster.Name, + }, + }, + } + + context := api.Context{ + Cluster: *c.ManagedCluster.Name, + AuthInfo: *c.ManagedCluster.Name, + } + + i.Clusters[*c.ManagedCluster.Name] = &cluster + i.AuthInfos[*c.ManagedCluster.Name] = &user + i.Contexts[*c.ManagedCluster.Name] = &context + + return nil +} + +func (program *Options) ReadConfig() (*api.Config, error) { + if _, err := os.Stat(program.KubeConfig); os.IsNotExist(err) { + c := api.NewConfig() + return c, nil + } else { + c, err := clientcmd.LoadFromFile(program.KubeConfig) + if err != nil { + return nil, err + } + return c, nil + } +} + +func (program *Options) WriteConfig(config *api.Config) error { + newFile := program.KubeConfig + ".tmp" + bakFile := program.KubeConfig + ".bak" + + err := clientcmd.WriteToFile(*config, newFile) + log := log.With().Str("kubeconfig_file", program.KubeConfig).Logger() + + if err != nil { + return err + } + + if _, err := os.Stat(bakFile); err == nil { + err = os.RemoveAll(bakFile) + if err != nil { + return errors.Wrap(err, "Failed to remove config backup file") + } + } + + if _, err := os.Stat(program.KubeConfig); os.IsNotExist(err) { + log.Debug().Msg("No existing config file. Copying new to config") + return os.Rename(newFile, program.KubeConfig) + } + + if err := os.Rename(program.KubeConfig, bakFile); err == nil { + if e2 := os.Rename(newFile, program.KubeConfig); e2 == nil { + return nil + } else { + if restoreErr := os.Rename(bakFile, program.KubeConfig); restoreErr != nil { + return errors.Wrap(restoreErr, "Error restoring kubeconfig. Backup left in "+bakFile) + } else { + return errors.Wrap(e2, "Error saving new kubeconfig") + } + } + } else { + return err + } +} diff --git a/program/azure/program.go b/program/azure/program.go new file mode 100644 index 0000000..5bcd01e --- /dev/null +++ b/program/azure/program.go @@ -0,0 +1,151 @@ +package azure + +import ( + "fmt" + "io" + "os" + "runtime" + "strings" + "sync" + + "github.com/alecthomas/kong" + "github.com/mattn/go-colorable" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +type Options struct { + Version bool `help:"Show program version"` + + KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` + Subscriptions []string `group:"Input" help:"List of Azure subscriptions to check"` + SubscriptionFile string `group:"Input" help:"File containing list of Azure subscriptions" type:"path"` + Locations []string `group:"Input" help:"List of Azure locations to check" env:"AZURE_LOCATIONS" default:"eastus,westus,centralus,northeurope,westeurope"` + ResourceGroups []string `group:"Input" help:"List of Azure resource groups to check"` + + Debug bool `group:"Info" help:"Show debugging information"` + OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` + Quiet bool `group:"Info" help:"Be less verbose than usual"` +} + +func (program *Options) Parse(args []string) (*kong.Context, error) { + parser, err := kong.New(program, + kong.ShortUsageOnError(), + kong.Description("Download kubeconfigs in bulk by examining AKS clusters across multiple subscriptions and locations"), + ) + if err != nil { + fmt.Println(err) + return nil, err + } + return parser.Parse(args) +} + +func (program *Options) Run(options *Options) error { + config, err := program.ReadConfig() + if err != nil { + log.Error().Err(err).Msg("Failed to read kubeconfig file") + return err + } + + clusters := make(chan AzureClusterInfo) + wg := sync.WaitGroup{} + + for sess := range program.getUniqueAzureSessions() { + wg.Add(1) + go func(sess *azureSessionInfo) { + defer wg.Done() + program.getClustersFrom(sess, clusters) + }(sess) + } + + go func() { + wg.Wait() + close(clusters) + }() + + for c := range clusters { + resourceGroup := "" + if id := *c.ManagedCluster.ID; id != "" { + parts := strings.Split(id, "/") + for i, part := range parts { + if part == "resourceGroups" && i+1 < len(parts) { + resourceGroup = parts[i+1] + break + } + } + } + + if err := captureConfig(c, resourceGroup, config); err != nil { + stats.Errors.Add(1) + log.Error().Err(err).Msg("Error capturing cluster configuration") + } + } + + if err := program.WriteConfig(config); err != nil { + stats.Errors.Add(1) + log.Error(). + Err(err). + Str("file", program.KubeConfig). + Msg("Error saving kubeconfig") + } + + stats.Log() + if stats.Errors.Load() > 0 { + return errors.New("Errors encountered during run") + } + return nil +} + +func (program *Options) AfterApply() error { + program.initLogging() + if len(program.Locations) < 1 { + return errors.New("Must specify at least one location") + } + if len(program.Subscriptions) < 1 && program.SubscriptionFile == "" { + return errors.New("Must specify either subscriptions or subscription file") + } + return nil +} + +func (program *Options) initLogging() { + if program.Version { + fmt.Println(Version) + os.Exit(0) + } + + switch { + case program.Debug: + zerolog.SetGlobalLevel(zerolog.DebugLevel) + case program.Quiet: + zerolog.SetGlobalLevel(zerolog.WarnLevel) + default: + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + + var out io.Writer = os.Stdout + if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { + out = colorable.NewColorableStdout() + } + + if program.OutputFormat == "terminal" || + (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) + } else { + log.Logger = log.Output(out) + } + + log.Logger.Debug(). + Str("version", Version). + Str("program", os.Args[0]). + Msg("Starting") +} + +func isTerminal(file *os.File) bool { + if fileInfo, err := file.Stat(); err != nil { + log.Err(err).Msg("Error running stat") + return false + } else { + return (fileInfo.Mode() & os.ModeCharDevice) != 0 + } +} \ No newline at end of file diff --git a/program/azure/stats.go b/program/azure/stats.go new file mode 100644 index 0000000..4ae8995 --- /dev/null +++ b/program/azure/stats.go @@ -0,0 +1,23 @@ +package azure + +import ( + "github.com/rs/zerolog/log" + "sync/atomic" +) + +type Stats struct { + Subscriptions, UniqueSubscriptions, UsableSubscriptions, Locations, Clusters, Errors atomic.Int32 +} + +func (s *Stats) Log() { + log.Info(). + Int32("subscriptions", s.Subscriptions.Load()). + Int32("unique_subscriptions", s.UniqueSubscriptions.Load()). + Int32("usable_subscriptions", s.UsableSubscriptions.Load()). + Int32("locations", s.Locations.Load()). + Int32("clusters", s.Clusters.Load()). + Int32("fatal_errors", s.Errors.Load()). + Msg("Statistics") +} + +var stats Stats diff --git a/program/azure/version.go b/program/azure/version.go new file mode 100644 index 0000000..116d04f --- /dev/null +++ b/program/azure/version.go @@ -0,0 +1,13 @@ +package azure + +import "fmt" + +var Version = "unknown" + +type VersionCmd struct{} + +func (v *VersionCmd) Run(program *Options) error { + _ = program + _, _ = fmt.Println(Version) + return nil +} diff --git a/program/gcp/gcp.go b/program/gcp/gcp.go index 79f77b4..3994780 100644 --- a/program/gcp/gcp.go +++ b/program/gcp/gcp.go @@ -99,6 +99,7 @@ func (program *Options) getClustersFrom(s *gcpSessionInfo, clusters chan<- GCPCl }(c) } } + func (program *Options) getUniqueGCPSessions() <-chan *gcpSessionInfo { sessions := make(chan *gcpSessionInfo) diff --git a/program/gcp/program_test.go b/program/gcp/program_test.go deleted file mode 100755 index 9360f1c..0000000 --- a/program/gcp/program_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package gcp - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestOptions_ReadConfig(t *testing.T) { - program := Options{KubeConfig: "testdata/config"} - - config, err := program.ReadConfig() - - assert.NoError(t, err) - assert.NotNil(t, config) - - assert.Equal(t, 6, len(config.Clusters)) - - c := config.Clusters["docker-desktop"] - assert.Equal(t, "https://kubernetes.docker.internal:6443", c.Server) -} diff --git a/program/testdata/config b/program/testdata/config deleted file mode 100644 index f2b98fa..0000000 --- a/program/testdata/config +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: v1 -clusters: -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1EUXdPREF3TlRBMU9Wb1hEVE14TURRd05qQXdOVEExT1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTEp4Cmp1TzhLK2plOSthb3M0MWNjbDZPRU9mUXFOQTlZcFpPb0hIdVNwcGR2R3pBZlpEak11ZFJJZGs0aDcyY0dkczYKU29HVU5IeFhjY3hodnBnT0FGVDJWcmhBdFpCRmJwQjdSZVJraTBCVU1JWm9GSjN0NzA2ZjdwaTZScTNJUC85LwovaVNweU1ST1c0Tk8zTVllbmNpY0JIN0NhZWovMTJZUjlMN0RZc3hBZU1QUDA4SkF2c043bU1BNXJreEhvOHJvCmFmN0hIbDN3Z2RTVXN2dmdJRm1vTFVpVWJxWlpkdE1CTzEyMVBlMXUvZUlVamIzbFhJNXdBUzB3VFkzemIzMnYKQm5vSXMzNkFnWnI1Z1QybEpzUFFZbVMrT2VsN3g1YURMb1dQQnhxSlQvUkZJLzQyRmViL2pQMUw1TGZZU0pLaQorVm5iUzFMMitBaFgxNmR5QUtNQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZHTUJDQnFTVGMxbkJ0eXAydDF4M2xlbktqdDFNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFCVi83ZldFdVhKVkFuRHJYbUFnQ0NZTENYeWFubU81eDRCS2lPVVpFNCs5Z3JGOWRRcgpiTWVCZ2c3WWJnUTJhMjBocmM2cDRRWnVlV09zK2FXdTd5blYyR2J2aEZLTVRCR1pGMHdUaCtJNDJLMnI1TVVoCk9FWk1VbTBJMU10aHpUWjQvQ1VCdk9FOE5GZ2dWc1g2VG1NRGRkNzYzVGpCeWhOYW1welhCK3J1MGNLRWN0SXYKTmdyOEpTVXpURTRHUFd2VG02NTladFdpUnlOSTJWUTNkNXhFUjdENkF1WkErekNrcDhCR3oyeHJZMytldVo3LwpWNTJiSU9COGJpdFdYUklSMHkyaStPVllFcE1QYkdJVE9rVG9nK2lNSGg3bHYyT0hCS29kSExYVVc1VDIzTFVlCmcyMmdRR1J4bk9IMm83eXJGVHBPaHpaUVY5NEp3MXNFOTg4YgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - server: https://kubernetes.docker.internal:6443 - name: docker-desktop -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTlRjeU16RXlPREl3SGhjTk1qSXdOekEzTWpJd01USXlXaGNOTXpJd056QTBNakl3TVRJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTlRjeU16RXlPREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTR3FXd2FSdS9iWkFPQW1xMkpZRk1FUllNYS9iVVNHeXBZRWp5d2xoSGEKcVg5ZmdWdGVzTFh4Ukp5ZW9CS0RDTU1jLzJPUjZiaDk4VmhYc3VTUjQ2bFVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVUVsVFRNL1RuY2xDOFNRVG5hd3lSCmYrYS9RZW93Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQU5NbzB2cDhDZkJoR2VzUlBKd1ZTcm1ldnhMdTMyeXIKZnBMWW41VUMzUjhrQWlFQWl6N2NXRU9oWllSU2JPNVZOTVU0ZzRBTkVuMFJwdjdyV0hCYi8vZWluUm89Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://0.0.0.0:60876 - name: k3d-k3d-dev -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTlRRMU5EYzNORGt3SGhjTk1qSXdOakEyTWpBek5UUTVXaGNOTXpJd05qQXpNakF6TlRRNQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTlRRMU5EYzNORGt3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSdFFPOTBxOUxjREpVM3NoTThTUjJobG5CYmZFc0d0aFlwbkhHRWZ0d3AKYUxkZ0wyU21DcDkrdDdFc2x2bmRHUUFuZG5saE5xdG1tQWtZOGZ2bHRHOWVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTl2RWtjdUZaS0orKzNXdWwwYlZ4CkFXaThkSTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnTnJFSk1pc3JFSzNQUVNBWGFuRTkrV2VMSWlaUHRmTi8KRENWRzFWZ3JiWFVDSVFEMUo4cGZtU216NG9WTjhDOUtpWVVyOU05R0ZndXM5bDN6MWFxbGFadW93QT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://0.0.0.0:63560 - name: k3d-k3d-notifications -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTlRRMk16QXpOelV3SGhjTk1qSXdOakEzTVRrek1qVTFXaGNOTXpJd05qQTBNVGt6TWpVMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTlRRMk16QXpOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTcFNuL0hWWWJpYkVrZFBZd0ZFVUNuVzM3MEl4MmwyTEMxUFQ2Y25BazMKOU1DbDFWcGt4VmxibDhuSGJEeE8wRVJOQVFSb0czWEp5QjhHTlc4NXUxTGVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXVyUWdmNmFBaDZmRWtERVZZRUp0CnFibHlJVnd3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUlLd0MwRy9XRVpqdTIwaTJwcHRHcWNhZndaZFdrai8KM2o5UHYvdmdHTlFCQWlFQXBoenMwdE54bVNlcDUzaENXOWNoSUI1bVc5MGp0R0lab3M4ZUIzUU5wQzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://0.0.0.0:62233 - name: k3d-notifications -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1EWXlOekl5TWpBMU4xb1hEVE15TURZeU5ESXlNakExTjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDlWCkNDTFVhNzVsRmJpL1lBcUdpVlhSM013Ym5CTTFJT1V2c09OWlRtQ250dDk4cFV0WTZFZk9xc01hUHpja21wRmEKSHRIRFc0cERUWmtZRnp2THlNNW5xZldKTjUxV1VSdExMQW1Hd3lLUTZTc0tOL1czZG9MR2lNQW1mU2wxNDdLUAprWTZQdjB1Sld4ZTRzNm9zbVBKTzZvNC9FNk11OEt4dkduWndNUm54c3ZjZ3hMYXVIQ1h3Q2owckR5aFZZN0thClJJQndCSXZpNkdEeVp1USthdHhOWlVLSGNmQkxwemc4QU5DZnVScWY3aE5NWU04ZzFvRHZJMkFEQUZzbGVPeEwKVDV4SlhkK3IzeE82VnNxWFpNYUtmTG1PTURYZDBtcHlXcGlnNlhjQ2MvcnZnNDFEeFpqNmZMbk5EdWZMY0VXZwpxaEo2WXZLaVBMVXhVaDJ0MTVjQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZOelUrNEdtcGRhb09VbjhRSi9mRGk1ZGtYalVNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSHRjd2tqcXNjZ0haakRKNWZESwoyOThqTkpNS29vY2VXWmQveXNIVHpsWEJ3NGJxWVBkcDEwSTJtVkpHbHRmR3QrZm9hY01INnlDV0RYdDdRZW41CnhRdmt1WVRndDh0OE4yQkMyUWFzVytkbUNueDFRZzhWWk5iOFoxeEhWUE1LQ2dmbzZPczBETVMxOHJOMHhsWXMKMnpHZTJ2dE8xTXBhT2djSDZES2M4T3RtVDdyUXVJRDdncXVDU3BuNk9yMlVHOVB3NFQzeG1WVVVzaHNPZVZCagpqRDBiL3JXUXhIbGhYNEo4MW9iOTZhdHlUcmhOVDdJWlI0VVdXNnVnOThPcUJ4amxXNWNrUXVlbXRPT29obVIwCnBRcGJReFppQVdNVFV5VWd5VGV1cmNBN095UWZVR1IvbGtlOFFtejU0aTczb3l6bkhiTUJocFU0aWFNM0pVRDgKeUw4PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - server: https://127.0.0.1:51890 - name: kind-kind -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTkRZek5EUTVOREl3SGhjTk1qSXdNekF6TWpJd01qSXlXaGNOTXpJd01qSTVNakl3TWpJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTkRZek5EUTVOREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTTlVtZXNMZ2VMR25acXlQandEVVRMdHpxdXRnSi84S1JlK2g0dE56eVIKcDErcEtrUE9TNkF0cCsxeUNDR241NXdPaXFGMWhDWS9iQkE1dUlITGRhOG9vMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWR6akgvYWVWZUorbEFHcHNHMlRzCmthbFJ3ZGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9DRWk2dDlMSU9uV3VWQzNsaHB1WFVrV09lYzkvY08KVHc2VHNnZnNSRGI3QWlBdDNRcDA4eFpyUExjdk80VVk2ZzRETG03MEoxYzRESEI2RlU1TWFlR2hYdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://127.0.0.1:6443 - name: rancher-desktop -contexts: -- context: - cluster: docker-desktop - user: docker-desktop - name: docker-desktop -- context: - cluster: k3d-k3d-dev - namespace: annotation-expander - user: admin@k3d-k3d-dev - name: k3d-k3d-dev -- context: - cluster: kind-kind - user: kind-kind - name: kind-kind -- context: - cluster: rancher-desktop - user: rancher-desktop - name: rancher-desktop -current-context: docker -desktop -kind: Config -preferences: {} -users: -- name: admin@k3d-k3d-dev - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJRFdreXVvV0w2QzR3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOalUzTWpNeE1qZ3lNQjRYRFRJeU1EY3dOekl5TURFeU1sb1hEVEl6TURjdwpOekl5TURFeU1sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJIbHo2QUdHR0t1SUtPU00KQTFycEZqSHFXYmdna2c1NCtxVFZ5bDVuZnRONmVWR2dSM05RdHJGN2Vna0xCTldxZHpiMjBLSDhVUTRObGhzYgpRMXZmUmlDalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCUktyTFNJaElGcTlmNGNvcWl6TTU4Wlo0Um91REFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQWpUQWlBanNzVEFnNkw1N2IrK1BHNjBDV2NFYzBORTFTSEw1ZHV6SFVJN0lDSUd1TEIrVFY4bXNPR0V2Nwp1MFRiUG11M0ZXRXNDamswcXFnOEU0a0hlNzlMCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUyTlRjeU16RXlPREl3SGhjTk1qSXdOekEzTWpJd01USXlXaGNOTXpJd056QTBNakl3TVRJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUyTlRjeU16RXlPREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSOU85QjMzVnpRSTBZY0FXOUdGczIxY21nY0FwYlNHUk9OaGpMOUdRSHYKempTMUVsdENUNXJxUFA5RTlOcjVIS2gzY2FpS0w1SjVIVmZTVVYxWHpBaUVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVVNxeTBpSVNCYXZYK0hLS29zek9mCkdXZUVhTGd3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVTI0ZTI0VWN0WkxVODhNNjZTbVFGK0xWTVpIQXBzRkMKSUtrdUcwZTB2OVVDSUdyeG9IdmFrZDdUeGlZUTFYcS9oK3c1ZE9lb3R6bE1LVFMrVmtyN3U3clEKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= - client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUhacTFIVDc3S2dPdGtRbHNwSGZMOGRQaUc4SDlJaEh1R2NHaXBOVWduZlRvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFZVhQb0FZWVlxNGdvNUl3RFd1a1dNZXBadUNDU0RuajZwTlhLWG1kKzAzcDVVYUJIYzFDMgpzWHQ2Q1FzRTFhcDNOdmJRb2Z4UkRnMldHeHREVzk5R0lBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= -- name: docker-desktop - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGVENDQWYyZ0F3SUJBZ0lJVmkveFpGMi93cDB3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRBME1EZ3dNRFV3TlRsYUZ3MHlNakEyTURReE9UQTFORFZhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzc0bjFlZmwya0V0dGsKcVY4cGRaNGxwNkhkbnN5NVdNU2w0d1JxT3MzOTNMZG53NHRIM2w5YllxTWk0eHJ0TTJ4T1dlM0NTa0g0d2xaWgpGVlBsZG5TTW5aRDdZaTBSNHFteWdnWkxYeU4reWZoZFdDVGtscWd4R2RHNzVUQWM0WWpDN1Z6RE01UkcxVi84CkdJYVJac2h0NXNPWlRUaFowbHBTRnZIdzJRNlBzK0xuYXovSzlxalAxMC9CYVFocG55aWZpY3lkMHZNcGpCRSsKTTVkWjBUVklwUEI4TE5NWUV2RVhRUEpUbzF0QTFzUzhFL29mdy94dktjN1M2ZTJTbXlEZDlvZWJiWEhXUWV4ZgpOR1M5N1I2bnlNY29iQWl1dkdiUG1oTG9NSHRRM05WM01xZUxHb3I1SlJzSnJGTlc1dlE1YXRQZHJrVkRqc0dECkpVTjhUMkFoQWdNQkFBR2pTREJHTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQWZCZ05WSFNNRUdEQVdnQlJqQVFnYWtrM05ad2JjcWRyZGNkNVhweW83ZFRBTkJna3Foa2lHOXcwQgpBUXNGQUFPQ0FRRUFzRjdvWUNYbks0R2g1L3p3TDhEVnR1Yk96L2ZRejNibXFsVmhLZUMvZG5RRUk0VVhRc1dSCjVxMkQrQjFlNXptRWtub0hlWE16blUwOVBubVhmTHhtSGRwcThOcjZJRjFwMjhwdHA0M28xR080L3RuZlNoU3MKSTBFbEtaY0tVOGxZWkIxUDVsM0g1Vms1a3lROTQyWEZYZ3VOZkJwTzROaU9nMUxnSllyVVp2NWdXM0hPbG9LKwowMFJ2ZVlHTUZ4bHROUWExc1llVy9pM1NOZGFacVBkK25BYStmZHhQa2t6YThGeERMYy9QQy82Tmw0dVNGRXVHClkvZEpSaGcxQy9QaWFQNFc5REw0Y2VnYWNBSzBqNUZsa25IQ0dXYXowaHZyQlhTSDBhODhWbkJrWU5rQXoxN0wKVVd6b3VHU1ovbExQaTFVemYrMlZpVGFmMURQbjRQT2pxQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdStKOVhuNWRwQkxiWktsZktYV2VKYWVoM1o3TXVWakVwZU1FYWpyTi9keTNaOE9MClI5NWZXMktqSXVNYTdUTnNUbG50d2twQitNSldXUlZUNVhaMGpKMlErMkl0RWVLcHNvSUdTMThqZnNuNFhWZ2sKNUphb01SblJ1K1V3SE9HSXd1MWN3ek9VUnRWZi9CaUdrV2JJYmViRG1VMDRXZEphVWhieDhOa09qN1BpNTJzLwp5dmFvejlkUHdXa0lhWjhvbjRuTW5kTHpLWXdSUGpPWFdkRTFTS1R3ZkN6VEdCTHhGMER5VTZOYlFOYkV2QlA2Ckg4UDhieW5PMHVudGtwc2czZmFIbTIxeDFrSHNYelJrdmUwZXA4akhLR3dJcnJ4bXo1b1M2REI3VU56VmR6S24KaXhxSytTVWJDYXhUVnViME9XclQzYTVGUTQ3Qmd5VkRmRTlnSVFJREFRQUJBb0lCQVFDQU9udkYyZ2lleWgvaAorNVp1L1U4SjgvYU5BcXhHYzBjQzBJVmEydmRyb3crcFNkUHRJTitEeHpYL3dXL0hJNmx2Y0FETThWRkZMWjB3Ci8xVmE3aFZ0UXJ5SVZPRGRyQXlWUUJoN3FGclpWWTZrWmx3SGhxc1RsOE9Fc1U2ODUxQW5lVWd1RVMzSmlpY1EKTHN6b3YzVmJUSHVDVFNtQVpBZ3dybWhCTUdLM3Y2bkRoOTNOVG5LYnA1TE02SGwrQnRzSElMeEVmT0RQUjFBaAp5QnlETXhYZXlFTnE2dHV2N0ZVZDFmSkZvb3Bob2xPYWhnUTNDVk9LOE9CQVRUa0RJN0xQOVNMeGNlRTVCMHRqCkFBRStzODYvV3VTaVhiTXBYbzV6amUzQlY5R3lxQnA5dmYvU2IzY0JwVWhyOHpVY1hlaE9xRzVtMUhCVTEwZlkKM2FMSmg3b0JBb0dCQU0xbGQvbHFzeDVqazloQUdoZjN0ZHZ6QjZYOXlOeXZIMXdZRkFvb2VEaC9ST0syczNhVgpVQVRLNG01cDMyZXBPY0daR2VpSUFMK2hQV25EL0V5SFBsMldVYkJEaXdyTnIwMFNXSkNIRGpCZTJhOFF4MnpzCktvVDliSGxLRGdlRFBXOGVhYjUra3lyTTdGVUtTQU1EdDRYVTRPOHMzYmI3NExKcXFDRzVWSjBKQW9HQkFPb3MKaSt5T2NJRkEyQnBraU9wMGtLWHRERHBJdHlLNXY2SUEyYndiTy85aXhuZjdKYmNFZW5VeEQrV2FuYzlsVzJuKwpoVG1za0gxaSs4TGU5UDVQN1BYbWUzcG02OFlTalRHcndlODhrTDFkaDNRTTB0NUxqcTJRb1crV3IxNjZBOS90CkN3cS9EMC9HT2tYSTFuVDFLbDRvWG9EVTRSY0tnbkVzSlcxY3VJaFpBb0dCQUlQSENRMUZycnZkaVZwaDdodlkKZXA0dU00Y0U1eHpuWWtoVXptNmgvbGZKRjZvWks5VDJBUXZDR3pwWnhOS2VHUWNqc2lLbE4wMjdKa3E0U2lQVApWV2ozNjFzYVNwZlkxUkt1cFpHOGhKYjJFOVdrL3ozYjIzL05sQTNSUEdVSGg1L0dOSFBhZzVNZlBMVm16a0xZCjNLTUpySExuSnl5aU4weGc3NVFMY3NUNUFvR0FaMWl2aS8rMDRTWTMrVFhqUXUyY3BNMi85cnorcmNHaU5DSUIKOWNWakxpdHVnYWNDRndwUDdRWnhDSmk3Q3J1bG8xcWplemVRTVBZTlBBeGZ2UmlTOEZiK3FaOWdjSExoZzBsVQo5bnNaT2FsY1RUZ1htK2xtbDJ6Y0FqNVRPUkFKQTdzK2cwaWN2azB5akhlOVJiS3BOeExEMmtHWThTMC9NaWtVCmYzWkxMa2tDZ1lFQWdnT0R3bVR5MUMxVUNtZy8xUjVxenZFVEdjbi9uQkFZSkVOMjdGVUptbVhJMmF0K2JGUGsKQkVmWUVsSUhFNTRlbmZULzR3VnQ0eFprRnBSdHM4dzRZamR0N3Q1UWZ0Q0tTMXhzV3R6Yzk3Rk9tb0h3UlIxeAowb0ZEcks0YjRpTWFsb09VREF4eUUyV3JxaEc4OHhyMjVsNjR0T1ZwaDVzTFFnWkNROEVTRVZFPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= -- name: kind-kind - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJR0ZTRXhOcTI3cG93RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBMk1qY3lNakl3TlRkYUZ3MHlNekEyTWpjeU1qSXdOVGxhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXZEUExRdVJtOHRkeUZlNjYKaWlVMjZaU2g2L2JicVBwSlQ4RDRqamlPN2xzOW9od2pLUVI3NG41YklGRW9ybXNSK0RlSFNpQ3ZpRXlTaWVzNAp6cEVQczJONGs4cTRVdzVzNjFxNm9Jdmd4ZHFKa1JLREFWU3JNaG50dmRtazJCZE01SkZ4OXkwSzhsN0ExWlRYClFEelJocE1YK2RLOU9VYWpSSHhvZjI4d0NHL1ZiOG5LNEZoUEFad0V4U0JOZ29kaFo3VWQ0VnRkU05TVVZBVC8KMGZ4SENWWEk4ak9jaHhkZzY1OTBOSndpbG95RVlyNUR1c0RqWitaemxMMVlvK1JtelE0YkRpb0ZWa3R3Z2U3TQpOcVZEZ0N6b1J2clQxWi9qdWd1dDRzVDZDMzIxazR4TVZPVU5ZVUFrV0NURm1XMVowSzZUaElGM1lXTWRDcHRmCnVVUVU2UUlEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JUYzFQdUJwcVhXcURsSi9FQ2YzdzR1WFpGNAoxREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBSSs1dlVCajFYVEZ2RVovS25lM0tmUGZIc2l2TDFpMXFOSkpECldqMmJWakxWcWc1aVRPdDNXbVVmRFVOdUJwUzlybGhxVG53dFZlTHFEeFIrRjhrNDlPRE1Yb0NxTlcwRUtWVVIKdVB6aHNwYnhsK2pBRG8yWFpzcVorSWVjbDI2NmJXQVVjcWJBTTk4OVh3VlMzeHZZalB5c3RlbksrT3ZvZEdyZQpRck0zbXFiY2kzVUcydktNb1NSblBLa25NU0gzOE53dms2RW9mQlRHZjlkNi91RytjMzNOcy9FNDEzMnhNcWtxCk5DWFkrOFk3VERSYXNGT2cwU1d0Tzd0dEVWOGlwK0JXbDF3c2JoeEM4QTNlZ0Q0LzRQQlpNcklwTkRsUUJ0djUKNEF6TmdjSjhDd1VVWjNwY2J6b1BQVkRyYjQrNWJ3Snd2RnluZTFNSi9EdWhrc3pYaHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdkRQTFF1Um04dGR5RmU2NmlpVTI2WlNoNi9iYnFQcEpUOEQ0amppTzdsczlvaHdqCktRUjc0bjViSUZFb3Jtc1IrRGVIU2lDdmlFeVNpZXM0enBFUHMyTjRrOHE0VXc1czYxcTZvSXZneGRxSmtSS0QKQVZTck1obnR2ZG1rMkJkTTVKRng5eTBLOGw3QTFaVFhRRHpSaHBNWCtkSzlPVWFqUkh4b2YyOHdDRy9WYjhuSwo0RmhQQVp3RXhTQk5nb2RoWjdVZDRWdGRTTlNVVkFULzBmeEhDVlhJOGpPY2h4ZGc2NTkwTkp3aWxveUVZcjVECnVzRGpaK1p6bEwxWW8rUm16UTRiRGlvRlZrdHdnZTdNTnFWRGdDem9SdnJUMVovanVndXQ0c1Q2QzMyMWs0eE0KVk9VTllVQWtXQ1RGbVcxWjBLNlRoSUYzWVdNZENwdGZ1VVFVNlFJREFRQUJBb0lCQVFDQ1gvNzNIL0tSVjk2WQpnd0NHb3JFZnJ1OWMzMjNiNlViUUVRWWV2OStFRDREcXhFTTNMdndETXpVOWRmdVFtR3VpWUJWQXJOUHN2OTdqCjNDZEdNZ2VHWmVvcHQ4MW8vQjFJRFZvcnpicXdWV3ZuSzhQRk1EcElRdDRISHJhclc3alF2bEVNd2c5TFI0aWUKYkxXUXNwRVRnT0tqRmZJdnMxZDNJaGZoNzh4K2lvcFpTb3lkWjhMM2puV09EU3Bod2pOa1ZCb21QblRSdFd0WQp0emVVbXdXUFN6cEdiTlp1YUdBNWN0SDUyc0t4SzEyNlBoUi9tOEtEdTVIcnJHMkRVUEN2bkx3cTNqdENNTkxkClFiWU01UktCR1lETlNOWG8rTkJQbXVybTFWNnl5NUk0L045Zm8yY0lnRzVpd1FSM3g5M0szYlM4TmJxejc4SUsKbkJUcWdwZ0JBb0dCQU4wd2JTbnhCVmxzdUVXUVA2K0dZeVl6L2M1bzZPZXpKTGhIOElHU3ZWSmFNVWlUR1VsSwowL2kzOGllNVI1dXlTSmpZNmQvbXgrbkFkdm1NMGxwaDNrMUI0ZVJ3Si9NZndrb05RREMzbkFnK3B5ZVFTanYrCkdwSjFwRkF0TFh4QTNLUDlESUN0a0NRK0E0RDNMRnRwYmFSdTliSHFuQ1JQanh3dmhLWHhRUC9KQW9HQkFOblMKV1I4eXlMMWcweEtMYmF5UHd0ckR6UGpNK3VaekI1MkkrYjI5bGJDOE9GZFAzRkVCdDVMR2lkS29kVzZDa1ZYVQowNG82MG52NjVtWjFHQUdrZ2dvei9VY0tINkQ3OWJPcFRMNndvN3l2N2ZKTDU2ZVRPekc3bkpUMW1meUJSRzNuCkVpMEZYdU0weWFpamd0eTN0ZmVXaHNRaUtOeEpCM0MzTVJLY2NEd2hBb0dCQUpBeDFiaVY2NHJVbVh5Z0FTU0kKVTNMQVNpR1p1dHZ4cVdxSlczemRFSmJ6UFZXMnI3RmNHcnYyRHVOZW9oUWFNY0dUQjBSenlyZlVSTHVDMWs4ZApwakp2VkxmN2RjL0ZVSCsrQzY2MncvMkwrcVNNS0JqY0dxNklDc3BKNlhFclI2dVFkK29iTy9Da2pOd3lSclNCCkVuMHo2UEJIcGVyMkFyRzVPSnlGbm40aEFvR0FWNFpjWlZya3FxUWpiRW8yVWFhYXA0a1I3YzkvNmVYQzQ1eE0KcmQ5bUxOUVUxNUN4ZUtOMS9qWmszdzd0MFBBS01qRUlwM3luWTBZWUdvZG1sQkprWm5FcGpzZ1VtWDFrNTBKYQpJaTJjUFk5N3oyYU11UnRDSitPNU1Xblc3RTJORDVSUUMyTWpFNno4Y3hUdzJudS9WTE5aaDZvcVBRbmpRWGw2CjhIMkg0OEVDZ1lBMFNZRUdwQ21UUXFOYTBzVGNsSXpUUzVGVlNBQTVEUXh3MXFNUFNKK04rRnVydWZVTlVrUkIKbnlDVmppVVd3dnEyL0FxVnNOc0VjemhuVVBOR0txUkVOWlZsMmgxTDNzY0NoNFBUamI4MGl0UWtrMTR3dEZIeQpVeWdIUS9hZDdpdmhiYUwyMjM3YmJDdDBzV3o5Y0hrTG5vZFBkSnpjbXhKWWx3WG9wUmRhZGc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= -- name: rancher-desktop - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJSHpRSXpiWFgwRDh3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOalEyTXpRME9UUXlNQjRYRFRJeU1ETXdNekl5TURJeU1sb1hEVEl6TURNdwpNekl5TURJeU1sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJEbGt5UG1XenBIamdDWFQKMHVLWlluTkxib25Cd2poMWd4NnZLZmRHSlN6Nnc2NENuUUZxZGU5TldmWEFmWG5VQVZmdGF2R1BUQjRwYmYzaQpYY09VRkh5alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCU3VRb2ZXa2htZFNyZjFJV1dkcTlsN2NyMGtsekFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQXdVZUVXaDFNVzFpZGplSExLL3dQNkVpQkRTVkM0YkxkRHhwRDlYTlpSM1VDSUIxYzNpRjBrQ1lxSlBuOQpzdXF5YmVnY2plSWJ2NHMrNy9oWVdFUnB2blZ3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUyTkRZek5EUTVOREl3SGhjTk1qSXdNekF6TWpJd01qSXlXaGNOTXpJd01qSTVNakl3TWpJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUyTkRZek5EUTVOREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSTXZBbTNST3RDTHhsWVpDM25ObUFtSk1ZVGJNSzEyYXlUR3lzK2F3cGEKTjBhem1jVytVWjNhQ0VXREw3TG1lV2szR2NGZEtGbmlwR1I2b0xtVDhaS3hvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXJrS0gxcElablVxMzlTRmxuYXZaCmUzSzlKSmN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnRGw5MlNrcUZBdGh6QUt6MGltV2REcEVTMkpkbDFnRjgKMTFLK1IranQrTTRDSVFEN3BuSnBsNDhGRVBXUXpYOU50ZjRZSCs0R1R1R2VyUVVYa3JlTDArb0ZaZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5qNjBXYzIyenIvZmMvMnVMdk5pa0VIbnpyd0dTbm5ZNDhpdWdKdEpodjRvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFT1dUSStaYk9rZU9BSmRQUzRwbGljMHR1aWNIQ09IV0RIcThwOTBZbExQckRyZ0tkQVdwMQo3MDFaOWNCOWVkUUJWKzFxOFk5TUhpbHQvZUpkdzVRVWZBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=