diff --git a/cmd/manager/main.go b/cmd/manager/main.go index addd2f2e..66973194 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -23,6 +23,7 @@ import ( "github.com/stolostron/managedcluster-import-controller/pkg/constants" "github.com/stolostron/managedcluster-import-controller/pkg/controller" "github.com/stolostron/managedcluster-import-controller/pkg/controller/agentregistration" + "github.com/stolostron/managedcluster-import-controller/pkg/controller/flightctl" "github.com/stolostron/managedcluster-import-controller/pkg/controller/importconfig" "github.com/stolostron/managedcluster-import-controller/pkg/features" "github.com/stolostron/managedcluster-import-controller/pkg/helpers" @@ -283,6 +284,9 @@ func main() { // with involvedObject set to the ManagedCluster. mcRecorder := helpers.NewManagedClusterEventRecorder(ctx, clientHolder.KubeClient) + // Init flightctler + flightctl := flightctl.NewFlightCtl(clientHolder) + setupLog.Info("Registering Controllers") if err := controller.AddToManager( ctx, @@ -301,6 +305,7 @@ func main() { KlusterletConfigLister: klusterletconfigLister, ManagedClusterInformer: managedclusterInformer, }, + flightctl, mcRecorder, ); err != nil { setupLog.Error(err, "failed to register controller") diff --git a/go.mod b/go.mod index 869085d1..18534839 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,11 @@ module github.com/stolostron/managedcluster-import-controller -go 1.22.0 +go 1.22.5 + +replace github.com/flightctl/flightctl/lib => github.com/xuezhaojun/flightctl/lib v0.0.0-20241125124411-7eec33f53a61 require ( + github.com/flightctl/flightctl/lib v0.0.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 @@ -40,6 +43,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/RangelReale/osincli v0.0.0-20160924135400-fababb0555f2 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -52,6 +56,8 @@ require ( github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/getkin/kin-openapi v0.128.0 // indirect + github.com/go-chi/chi v1.5.5 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect @@ -76,6 +82,7 @@ require ( github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/yaml v0.3.1 // indirect github.com/itchyny/gojq v0.12.7 // indirect github.com/itchyny/timefmt-go v0.1.3 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -91,21 +98,25 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/microcosm-cc/bluemonday v1.0.18 // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/openshift/assisted-service/models v0.0.0 // indirect github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/samber/lo v1.47.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.8.1 // indirect diff --git a/go.sum b/go.sum index 9749cbe9..9ca3bb8b 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,9 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RangelReale/osincli v0.0.0-20160924135400-fababb0555f2 h1:x8Brv0YNEe6jY3V/hQglIG2nd8g5E2Zj5ubGKkPQctQ= github.com/RangelReale/osincli v0.0.0-20160924135400-fababb0555f2/go.mod h1:XyjUkMA8GN+tOOPXvnbi3XuRxWFvTJntqvTFnjmhzbk= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -21,6 +24,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -62,9 +66,13 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= +github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE= +github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -105,6 +113,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -168,6 +178,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= +github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ= github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw= github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= @@ -228,6 +240,7 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 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/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= 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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -259,8 +272,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= -github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -275,6 +288,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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= @@ -282,6 +297,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -315,6 +332,8 @@ github.com/openshift/hypershift/api v0.0.0-20241022184855-1fa7be0211e4 h1:hUOaw1 github.com/openshift/hypershift/api v0.0.0-20241022184855-1fa7be0211e4/go.mod h1:aTvlq6HevyNZ01GIaPBnnIe+HoLym3B2TIUBEol2reY= github.com/openshift/library-go v0.0.0-20240207105404-126b47137408 h1:Evg6GEvEuyj9toFX14YenXI6hGRnhLWqYx/rHO7VnQ4= github.com/openshift/library-go v0.0.0-20240207105404-126b47137408/go.mod h1:ePlaOqUiPplRc++6aYdMe+2FmXb2xTNS9Nz5laG2YmI= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -337,6 +356,8 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= @@ -353,6 +374,7 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA= github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stolostron/cluster-lifecycle-api v0.0.0-20240918064238-a5e71b599118 h1:etBoN2TXjxiTNZ/pmvbHuKSvJWm7ml+QGWrHPTeHQ+g= github.com/stolostron/cluster-lifecycle-api v0.0.0-20240918064238-a5e71b599118/go.mod h1:Sflr4YW8MRsymgNLJDcOAv4oyfeiTRyEDR7PRBkg788= @@ -367,8 +389,12 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xuezhaojun/flightctl/lib v0.0.0-20241125124411-7eec33f53a61 h1:Fkx2DwvpT0iKRBS0c2xvdeH7mGjxsoyIAG2H6DdmTSs= +github.com/xuezhaojun/flightctl/lib v0.0.0-20241125124411-7eec33f53a61/go.mod h1:D9FxWj70QGZfKKgoH6QwgC6/1HVx3aLgccRmSftdAig= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -438,7 +464,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 04a337f1..7baf2a5d 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -16,6 +16,7 @@ import ( "github.com/stolostron/managedcluster-import-controller/pkg/controller/clusterdeployment" "github.com/stolostron/managedcluster-import-controller/pkg/controller/clusternamespacedeletion" "github.com/stolostron/managedcluster-import-controller/pkg/controller/csr" + "github.com/stolostron/managedcluster-import-controller/pkg/controller/flightctl" "github.com/stolostron/managedcluster-import-controller/pkg/controller/hosted" "github.com/stolostron/managedcluster-import-controller/pkg/controller/importconfig" "github.com/stolostron/managedcluster-import-controller/pkg/controller/importstatus" @@ -36,6 +37,7 @@ func AddToManager(ctx context.Context, manager manager.Manager, clientHolder *helpers.ClientHolder, informerHolder *source.InformerHolder, + flightctler flightctl.FlightCtler, mcRecorder kevents.EventRecorder) error { AddToManagerFuncs := []struct { @@ -46,7 +48,19 @@ func AddToManager(ctx context.Context, csr.ControllerName, func() error { return csr.Add(ctx, manager, clientHolder, - []func(ctx context.Context, csr *certificatesv1.CertificateSigningRequest) (bool, error){}) + []func(ctx context.Context, csr *certificatesv1.CertificateSigningRequest) (bool, error){ + // Case 1: If flightctl is enabled and a csr is from a flightctl device, approve it. + func(ctx context.Context, csr *certificatesv1.CertificateSigningRequest) (bool, error) { + isFlightCtlEnabled, err := flightctler.IsFlightCtlEnabled(ctx) + if err != nil { + return false, err + } + if isFlightCtlEnabled { + return flightctler.IsManagedClusterAFlightctlDevice(ctx, csr.Spec.Username) + } + return false, nil + }, + }) }, }, { @@ -90,6 +104,14 @@ func AddToManager(ctx context.Context, return nil }, }, + { + flightctl.NamespaceControllerName, + func() error { return flightctl.AddNSController(ctx, manager, flightctler, clientHolder) }, + }, + { + flightctl.ManagedClusterControllerName, + func() error { return flightctl.AddManagedClusterController(ctx, manager, flightctler, clientHolder) }, + }, } for _, f := range AddToManagerFuncs { diff --git a/pkg/controller/flightctl/flightctl.go b/pkg/controller/flightctl/flightctl.go new file mode 100644 index 00000000..8a33ff04 --- /dev/null +++ b/pkg/controller/flightctl/flightctl.go @@ -0,0 +1,176 @@ +package flightctl + +import ( + "context" + "embed" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + flightctlapiv1 "github.com/flightctl/flightctl/lib/apipublic/v1alpha1" + flightctlcli "github.com/flightctl/flightctl/lib/cli" + "github.com/stolostron/managedcluster-import-controller/pkg/helpers" + authenticationv1 "k8s.io/api/authentication/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +const ( + FlightCtlNamespace = "flightctl" + FlightCtlServer = "https://flightctl-api.flightctl.svc.cluster.local" +) + +//go:embed manifests +var FlightCtlManifestFiles embed.FS + +// The flightctl-client's service account token has 2 usages: +// 1. delivered to the devices, used to access the agent-registration to get klusterlet manifests used for registration. +// 2. on the hub side, used for the import-controller to apply the flightctl's Repository resources and get devices. +var files = []string{ + "manifests/clusterrole.yml", + "manifests/clusterrolebinding_agentregistration.yml", + "manifests/clusterrolebinding_flightctl.yml", + "manifests/serviceaccount.yml", +} + +type FlightCtler interface { + IsFlightCtlEnabled(ctx context.Context) (bool, error) + ApplyRepository(ctx context.Context) error + IsManagedClusterAFlightctlDevice(ctx context.Context, managedClusterName string) (bool, error) +} + +func NewFlightCtl(clientHolder *helpers.ClientHolder) FlightCtler { + return &FlightCtl{ + clientHolder: clientHolder, + } +} + +type FlightCtl struct { + clientHolder *helpers.ClientHolder + token string +} + +var _ FlightCtler = &FlightCtl{} + +func (f *FlightCtl) IsFlightCtlEnabled(ctx context.Context) (bool, error) { + // If `flightctl` ns is not created, means the flightctl is not enabled. + ns := &corev1.Namespace{} + err := f.clientHolder.RuntimeClient.Get(ctx, types.NamespacedName{ + Name: FlightCtlNamespace, + }, ns) + + if errors.IsNotFound(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, nil +} + +func (f *FlightCtl) ApplyRepository(ctx context.Context) error { + expectedRepository := &flightctlapiv1.Repository{ + ApiVersion: "v1alpha1", + Kind: "Repository", + Metadata: flightctlapiv1.ObjectMeta{ + Name: ptr("acm"), + }, + Spec: flightctlapiv1.RepositorySpec{}, + } + expectedRepository.Spec.MergeHttpRepoSpec(flightctlapiv1.HttpRepoSpec{ + Type: flightctlapiv1.Http, + Url: FlightCtlServer, + HttpConfig: flightctlapiv1.HttpConfig{ + Token: &f.token, + }, + }) + return flightctlcli.ApplyRepository(ctx, f.token, FlightCtlServer, expectedRepository) +} + +func (f *FlightCtl) IsManagedClusterAFlightctlDevice(ctx context.Context, managedClusterName string) (bool, error) { + response, err := flightctlcli.GetDevice(ctx, f.token, FlightCtlServer, managedClusterName) + if err != nil { + return false, err + } + + if response.HTTPResponse.StatusCode == http.StatusNotFound { + return false, nil + } + + if response.HTTPResponse.StatusCode != http.StatusOK { + return false, fmt.Errorf("failed to get device %s, status code: %d", managedClusterName, response.HTTPResponse.StatusCode) + } + + return true, nil +} + +// if token is not set, create a token +func (f *FlightCtl) getFlightCtlClientServiceAccountToken(ctx context.Context) (string, error) { + // if token is not set, create one + if f.token == "" || f.isTokenExpired() { + token, err := f.createFlightCtlClientServiceAccountToken(ctx) + if err != nil { + return "", err + } + f.token = token + } + + return f.token, nil +} + +func (f *FlightCtl) createFlightCtlClientServiceAccountToken(ctx context.Context) (string, error) { + // Create a service account token with a year duration + tokenRequest := &authenticationv1.TokenRequest{ + Spec: authenticationv1.TokenRequestSpec{ + ExpirationSeconds: int64ptr(365 * 24 * 60 * 60), // 1 year in seconds + }, + } + + // Get the token from the service account + token, err := f.clientHolder.KubeClient.CoreV1().ServiceAccounts(FlightCtlNamespace).CreateToken(ctx, "flightctl-client", tokenRequest, metav1.CreateOptions{}) + if err != nil { + return "", fmt.Errorf("failed to create service account token: %w", err) + } + + return token.Status.Token, nil +} + +func (f *FlightCtl) isTokenExpired() bool { + if f.token == "" { + return true + } + + parts := strings.Split(f.token, ".") + if len(parts) != 3 { + return true + } + + claimsJSON, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return true + } + + var claims struct { + Exp int64 `json:"exp"` + } + if err := json.Unmarshal(claimsJSON, &claims); err != nil { + return true + } + + // Check if token will expire within 3 months (90 days) + threeMonthsInSeconds := int64(90 * 24 * 60 * 60) + return time.Now().Unix()+threeMonthsInSeconds >= claims.Exp +} + +func ptr(s string) *string { + return &s +} + +func int64ptr(i int64) *int64 { + return &i +} diff --git a/pkg/controller/flightctl/managedclustercontroller.go b/pkg/controller/flightctl/managedclustercontroller.go new file mode 100644 index 00000000..ea78a727 --- /dev/null +++ b/pkg/controller/flightctl/managedclustercontroller.go @@ -0,0 +1,81 @@ +package flightctl + +import ( + "context" + + "github.com/openshift/library-go/pkg/operator/events" + "github.com/stolostron/managedcluster-import-controller/pkg/helpers" + clusterv1 "open-cluster-management.io/api/cluster/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const ManagedClusterControllerName = "flightctl-managedcluster-controller" + +// ManagedClusterReconciler is responsible to set hubAcceptsClient to true if the managed cluster is a flightctl device. +type ManagedClusterReconciler struct { + clientHolder *helpers.ClientHolder + recorder events.Recorder + flightctl FlightCtler +} + +var _ reconcile.Reconciler = &ManagedClusterReconciler{} + +func (r *ManagedClusterReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + cluster := &clusterv1.ManagedCluster{} + if err := r.clientHolder.RuntimeClient.Get(ctx, request.NamespacedName, cluster); err != nil { + return reconcile.Result{}, err + } + + if cluster.DeletionTimestamp != nil { + return reconcile.Result{}, nil + } + + if cluster.Spec.HubAcceptsClient { + return reconcile.Result{}, nil + } + + isDevice, err := r.flightctl.IsManagedClusterAFlightctlDevice(ctx, cluster.Name) + if err != nil { + return reconcile.Result{}, err + } + if !isDevice { + return reconcile.Result{}, nil + } + + cluster.Spec.HubAcceptsClient = true + if err := r.clientHolder.RuntimeClient.Update(ctx, cluster); err != nil { + return reconcile.Result{}, err + } + + return reconcile.Result{}, nil +} + +func AddManagedClusterController(ctx context.Context, mgr manager.Manager, flightctler FlightCtler, clientHolder *helpers.ClientHolder) error { + err := ctrl.NewControllerManagedBy(mgr).Named(ManagedClusterControllerName). + Watches( + &clusterv1.ManagedCluster{}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.Funcs{ + GenericFunc: func(e event.GenericEvent) bool { return false }, + DeleteFunc: func(e event.DeleteEvent) bool { return false }, + UpdateFunc: func(e event.UpdateEvent) bool { + return !e.ObjectNew.(*clusterv1.ManagedCluster).Spec.HubAcceptsClient + }, + CreateFunc: func(e event.CreateEvent) bool { + return !e.Object.(*clusterv1.ManagedCluster).Spec.HubAcceptsClient + }, + })). + Complete(&ManagedClusterReconciler{ + clientHolder: clientHolder, + flightctl: flightctler, + recorder: helpers.NewEventRecorder(clientHolder.KubeClient, ManagedClusterControllerName), + }) + + return err +} diff --git a/pkg/controller/flightctl/manifests/clusterrole.yml b/pkg/controller/flightctl/manifests/clusterrole.yml new file mode 100644 index 00000000..7f8cb401 --- /dev/null +++ b/pkg/controller/flightctl/manifests/clusterrole.yml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: flightctl-client +rules: + - verbs: + - get + apiGroups: + - flightctl.io + resources: + - devices + - verbs: + - create + - update + - get + apiGroups: + - flightctl.io + resources: + - repositories diff --git a/pkg/controller/flightctl/manifests/clusterrolebinding_agentregistration.yml b/pkg/controller/flightctl/manifests/clusterrolebinding_agentregistration.yml new file mode 100644 index 00000000..079a191f --- /dev/null +++ b/pkg/controller/flightctl/manifests/clusterrolebinding_agentregistration.yml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: flightctl-agent-registration +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: managedcluster-import-controller-agent-regitration-client +subjects: +- kind: ServiceAccount + name: flightctl-client + namespace: "{{ .Namespace }}" diff --git a/pkg/controller/flightctl/manifests/clusterrolebinding_flightctl.yml b/pkg/controller/flightctl/manifests/clusterrolebinding_flightctl.yml new file mode 100644 index 00000000..60a9183b --- /dev/null +++ b/pkg/controller/flightctl/manifests/clusterrolebinding_flightctl.yml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: flightctl-client +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: flightctl-client +subjects: +- kind: ServiceAccount + name: flightctl-client + namespace: "{{ .Namespace }}" diff --git a/pkg/controller/flightctl/manifests/serviceaccount.yml b/pkg/controller/flightctl/manifests/serviceaccount.yml new file mode 100644 index 00000000..5b9afd9d --- /dev/null +++ b/pkg/controller/flightctl/manifests/serviceaccount.yml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: flightctl-client + namespace: "{{ .Namespace }}" diff --git a/pkg/controller/flightctl/nscontroller.go b/pkg/controller/flightctl/nscontroller.go new file mode 100644 index 00000000..2f9462dd --- /dev/null +++ b/pkg/controller/flightctl/nscontroller.go @@ -0,0 +1,111 @@ +package flightctl + +import ( + "context" + + "github.com/openshift/library-go/pkg/operator/events" + "github.com/stolostron/managedcluster-import-controller/pkg/helpers" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const NamespaceControllerName = "flightctl-namespace-controller" + +var log = logf.Log.WithName(NamespaceControllerName) + +var _ reconcile.Reconciler = &NSReconciler{} + +// NSReconciler is responsible for creating the FlightCtl rbac resources when namespace `flightctl` is created. +// The service account "flightctl-client" is binded `managedcluster-import-controller-agent-registration-client`. +// The token will be used in the `Repository` and delivered to the devices, to let the devices access the agent-registration and get klusterlet manifests used for registration. +type NSReconciler struct { + clientHolder *helpers.ClientHolder + flightctl FlightCtler + recorder events.Recorder + scheme *runtime.Scheme +} + +func (r *NSReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + log.Info("Reconciling FlightCtl namespace", "namespace", request.Name) + var err error + + // Get the FlightCtl namespace + // If ns found, create resources and set owner reference to the ns. + ns := &corev1.Namespace{} + err = r.clientHolder.RuntimeClient.Get(ctx, request.NamespacedName, ns) + if errors.IsNotFound(err) { + // if ns not found, delete the Repository we created + return reconcile.Result{}, nil + } + if err != nil { + return reconcile.Result{}, err + } + + if ns.Name != FlightCtlNamespace { + return reconcile.Result{}, nil + } + + // Create rbac resources and set owner reference to the ns. + objects, err := helpers.FilesToObjects(files, struct { + Namespace string + }{ + Namespace: FlightCtlNamespace, + }, &FlightCtlManifestFiles) + if err != nil { + return reconcile.Result{}, err + } + if _, err := helpers.ApplyResources( + r.clientHolder, r.recorder, r.scheme, nil, objects...); err != nil { + return reconcile.Result{}, err + } + + // Create Repository resources + err = r.flightctl.ApplyRepository(ctx) + if err != nil { + return reconcile.Result{}, err + } + + return reconcile.Result{}, nil +} + +func AddNSController(ctx context.Context, mgr manager.Manager, flightctler FlightCtler, clientHolder *helpers.ClientHolder) error { + err := ctrl.NewControllerManagedBy(mgr).Named(NamespaceControllerName). + Watches( + &corev1.Namespace{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request { + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{Name: o.GetName()}, + }, + } + }), + builder.WithPredicates(predicate.Funcs{ + GenericFunc: func(e event.GenericEvent) bool { return false }, + DeleteFunc: func(e event.DeleteEvent) bool { return false }, + CreateFunc: func(e event.CreateEvent) bool { return e.Object.GetName() == FlightCtlNamespace }, + UpdateFunc: func(e event.UpdateEvent) bool { return e.ObjectNew.GetName() == FlightCtlNamespace }, + }), + ). + Complete(&NSReconciler{ + clientHolder: clientHolder, + flightctl: flightctler, + scheme: mgr.GetScheme(), + recorder: helpers.NewEventRecorder(clientHolder.KubeClient, NamespaceControllerName), + }) + if err != nil { + return err + } + + return nil +}