From e686d30996a7805df02919f22a4b561a40897bdf Mon Sep 17 00:00:00 2001 From: RaphaelBut Date: Fri, 12 Jun 2026 11:39:38 +0200 Subject: [PATCH] Add srelib integration POC Demonstrate how to include and use the srelib plugin library in CAD. Adds an adapter that wraps srelib's v1.Client behind CAD's ocm.Client interface, a plugin launcher for managing the srelib process lifecycle, and a runnable example showing the end-to-end flow. Co-Authored-By: Claude Opus 4.6 (1M context) --- go.mod | 16 +++-- go.sum | 32 ++++++++-- pkg/srelib/adapter.go | 109 +++++++++++++++++++++++++++++++++ pkg/srelib/adapter_test.go | 85 +++++++++++++++++++++++++ pkg/srelib/launcher.go | 60 ++++++++++++++++++ poc/srelib-integration/main.go | 76 +++++++++++++++++++++++ 6 files changed, 370 insertions(+), 8 deletions(-) create mode 100644 pkg/srelib/adapter.go create mode 100644 pkg/srelib/adapter_test.go create mode 100644 pkg/srelib/launcher.go create mode 100644 poc/srelib-integration/main.go diff --git a/go.mod b/go.mod index 72b6b661..a153f609 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ module github.com/openshift/configuration-anomaly-detection -go 1.25.7 +go 1.26.3 require ( github.com/PagerDuty/go-pagerduty v1.8.0 - github.com/aws/aws-sdk-go-v2 v1.41.7 + github.com/aws/aws-sdk-go-v2 v1.41.12 + github.com/aws/aws-sdk-go-v2/config v1.32.16 github.com/aws/aws-sdk-go-v2/credentials v1.19.16 github.com/aws/aws-sdk-go-v2/service/bedrockagentcore v1.24.0 github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.55.11 @@ -13,6 +14,8 @@ require ( github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.49.0 github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7 github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 + github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-plugin v1.8.0 github.com/onsi/gomega v1.40.0 github.com/openshift-online/ocm-common v0.0.40 github.com/openshift-online/ocm-sdk-go v0.1.500 @@ -23,6 +26,7 @@ require ( github.com/openshift/hive/apis v0.0.0-20260430175100-52fd62d6be80 github.com/openshift/osd-network-verifier v1.7.0 github.com/openshift/osde2e-common v0.0.0-20260421185005-d390433565e5 + github.com/petrkotas/srelib v0.0.0 github.com/pkg/sftp v1.13.10 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/common v0.67.5 @@ -56,7 +60,6 @@ require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect @@ -67,7 +70,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssm v1.68.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect - github.com/aws/smithy-go v1.25.1 // indirect + github.com/aws/smithy-go v1.27.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect @@ -83,6 +86,7 @@ require ( github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/camelcase v1.0.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/getkin/kin-openapi v0.133.0 // indirect @@ -125,6 +129,7 @@ require ( github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/yamux v0.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -148,6 +153,7 @@ require ( github.com/oapi-codegen/runtime v1.2.0 // indirect github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/openshift-online/ocm-api-model/clientapi v0.0.454 // indirect github.com/openshift-online/ocm-api-model/model v0.0.454 // indirect @@ -220,3 +226,5 @@ require ( ) replace github.com/openshift/configuration-anomaly-detection => ./ + +replace github.com/petrkotas/srelib => ../srelib diff --git a/go.sum b/go.sum index be62530e..c619368a 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7D github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= -github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= +github.com/aws/aws-sdk-go-v2 v1.41.12 h1:DIKX2c31ekm9RA2D9FBj1EWXx++9AdAqRw+e78Tq2Ck= +github.com/aws/aws-sdk-go-v2 v1.41.12/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 h1:gx1AwW1Iyk9Z9dD9F4akX5gnN3QZwUB20GGKH/I+Rho= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10/go.mod h1:qqY157uZoqm5OXq/amuaBJyC9hgBCBQnsaWnPe905GY= github.com/aws/aws-sdk-go-v2/config v1.32.16 h1:Q0iQ7quUgJP0F/SCRTieScnaMdXr9h/2+wze1u3cNeM= @@ -112,8 +112,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= -github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= +github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -125,6 +125,8 @@ github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2y github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= 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= @@ -168,6 +170,9 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -356,10 +361,16 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.8.0 h1:ie8S6RRY8RvB2usYZv+AAZ/wBvx2AU5p5QeP5j/FORs= +github.com/hashicorp/go-plugin v1.8.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -369,6 +380,8 @@ github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc= github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg= github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= @@ -420,9 +433,13 @@ github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUt github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +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-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -469,6 +486,8 @@ github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//J github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.28.3 h1:4JvMdwtFU0imd8fHx25OJXoDMRexnf8v5NHKYSTTji4= @@ -598,6 +617,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= @@ -783,6 +803,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -804,8 +825,11 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/srelib/adapter.go b/pkg/srelib/adapter.go new file mode 100644 index 00000000..f30f067a --- /dev/null +++ b/pkg/srelib/adapter.go @@ -0,0 +1,109 @@ +// Package srelib provides an adapter that wraps the srelib plugin's v1.Client +// and exposes it as a CAD-compatible ocm.Client for cluster lookups. +package srelib + +import ( + "fmt" + + sdk "github.com/openshift-online/ocm-sdk-go" + amv1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + servicelogsv1 "github.com/openshift-online/ocm-sdk-go/servicelogs/v1" + hivev1 "github.com/openshift/hive/apis/hive/v1" + + v1 "github.com/petrkotas/srelib/sdk/v1" + + "github.com/openshift/configuration-anomaly-detection/pkg/ocm" +) + +// Adapter wraps a srelib v1.Client and implements CAD's ocm.Client interface. +// Methods that srelib supports are delegated; the rest return "not supported" errors. +type Adapter struct { + srelib v1.Client +} + +var _ ocm.Client = (*Adapter)(nil) + +func NewAdapter(client v1.Client) *Adapter { + return &Adapter{srelib: client} +} + +func (a *Adapter) GetClusterInfo(identifier string) (*cmv1.Cluster, error) { + return a.srelib.GetCluster(identifier) +} + +func (a *Adapter) GetOrganizationID(clusterID string) (string, error) { + cluster, err := a.srelib.GetCluster(clusterID) + if err != nil { + return "", err + } + cmv1Sub, ok := cluster.GetSubscription() + if !ok { + return "", nil + } + org, err := a.srelib.GetOrganization(cmv1Sub.ID()) + if err != nil { + return "", err + } + return org.ID(), nil +} + +func (a *Adapter) GetSupportRoleARN(internalClusterID string) (string, error) { + return a.srelib.GetSupportRoleArnForCluster(internalClusterID) +} + +// --- Methods below are not covered by srelib and return unsupported errors. --- +// In a full integration these would either stay backed by the existing OCM SDK +// client or be added to srelib over time. + +func (a *Adapter) GetClusterMachinePools(string) ([]*cmv1.MachinePool, error) { + return nil, fmt.Errorf("srelib adapter: GetClusterMachinePools not supported") +} + +func (a *Adapter) PostLimitedSupportReason(*cmv1.Cluster, *ocm.LimitedSupportReason) error { + return fmt.Errorf("srelib adapter: PostLimitedSupportReason not supported") +} + +func (a *Adapter) GetServiceLog(*cmv1.Cluster, string) (*servicelogsv1.ClusterLogsUUIDListResponse, error) { + return nil, fmt.Errorf("srelib adapter: GetServiceLog not supported") +} + +func (a *Adapter) PostServiceLog(*cmv1.Cluster, *ocm.ServiceLog) error { + return fmt.Errorf("srelib adapter: PostServiceLog not supported") +} + +func (a *Adapter) AwsClassicJumpRoleCompatible(*cmv1.Cluster) (bool, error) { + return false, fmt.Errorf("srelib adapter: AwsClassicJumpRoleCompatible not supported") +} + +func (a *Adapter) GetConnection() *sdk.Connection { + return nil +} + +func (a *Adapter) IsAccessProtected(*cmv1.Cluster) (bool, error) { + return false, fmt.Errorf("srelib adapter: IsAccessProtected not supported") +} + +func (a *Adapter) GetClusterHypershiftConfig(*cmv1.Cluster) (*cmv1.HypershiftConfig, error) { + return nil, fmt.Errorf("srelib adapter: GetClusterHypershiftConfig not supported") +} + +func (a *Adapter) IsManagingCluster(string) (bool, error) { + return false, fmt.Errorf("srelib adapter: IsManagingCluster not supported") +} + +func (a *Adapter) GetDynatraceURL(*cmv1.Cluster) (string, error) { + return "", fmt.Errorf("srelib adapter: GetDynatraceURL not supported") +} + +func (a *Adapter) CheckIfUserBanned(*cmv1.Cluster) error { + return fmt.Errorf("srelib adapter: CheckIfUserBanned not supported") +} + +func (a *Adapter) GetCreatorFromCluster(*cmv1.Cluster) (*amv1.Account, error) { + return nil, fmt.Errorf("srelib adapter: GetCreatorFromCluster not supported") +} + +func (a *Adapter) GetClusterDeployment(string) (*hivev1.ClusterDeployment, error) { + return nil, fmt.Errorf("srelib adapter: GetClusterDeployment not supported") +} diff --git a/pkg/srelib/adapter_test.go b/pkg/srelib/adapter_test.go new file mode 100644 index 00000000..3401fc87 --- /dev/null +++ b/pkg/srelib/adapter_test.go @@ -0,0 +1,85 @@ +package srelib + +import ( + "testing" + + amsv1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// mockSrelibClient is a minimal mock of srelib's v1.Client for unit testing. +type mockSrelibClient struct { + cluster *cmv1.Cluster + subscription *amsv1.Subscription + organization *amsv1.Organization + supportARN string + awsAccountID string +} + +func (m *mockSrelibClient) GetCluster(string) (*cmv1.Cluster, error) { + return m.cluster, nil +} + +func (m *mockSrelibClient) GetClusterAnyStatus(string) (*cmv1.Cluster, error) { + return m.cluster, nil +} + +func (m *mockSrelibClient) GetClusters([]string) ([]*cmv1.Cluster, error) { + return []*cmv1.Cluster{m.cluster}, nil +} + +func (m *mockSrelibClient) GetManagementCluster(string) (*cmv1.Cluster, error) { + return m.cluster, nil +} + +func (m *mockSrelibClient) GetSubscription(string) (*amsv1.Subscription, error) { + return m.subscription, nil +} + +func (m *mockSrelibClient) GetOrganization(string) (*amsv1.Organization, error) { + return m.organization, nil +} + +func (m *mockSrelibClient) GetSupportRoleArnForCluster(string) (string, error) { + return m.supportARN, nil +} + +func (m *mockSrelibClient) GetAWSAccountIdForCluster(string) (string, error) { + return m.awsAccountID, nil +} + +func TestAdapter_GetClusterInfo(t *testing.T) { + cluster, err := cmv1.NewCluster().ID("test-cluster-id").Name("test-cluster").Build() + require.NoError(t, err) + + adapter := NewAdapter(&mockSrelibClient{cluster: cluster}) + + got, err := adapter.GetClusterInfo("test-cluster-id") + require.NoError(t, err) + assert.Equal(t, "test-cluster-id", got.ID()) + assert.Equal(t, "test-cluster", got.Name()) +} + +func TestAdapter_GetSupportRoleARN(t *testing.T) { + adapter := NewAdapter(&mockSrelibClient{ + supportARN: "arn:aws:iam::123456:role/RH-Technical-Support-Access", + }) + + arn, err := adapter.GetSupportRoleARN("cluster-123") + require.NoError(t, err) + assert.Equal(t, "arn:aws:iam::123456:role/RH-Technical-Support-Access", arn) +} + +func TestAdapter_UnsupportedMethods(t *testing.T) { + adapter := NewAdapter(&mockSrelibClient{}) + + _, err := adapter.GetClusterMachinePools("x") + assert.ErrorContains(t, err, "not supported") + + err = adapter.PostLimitedSupportReason(nil, nil) + assert.ErrorContains(t, err, "not supported") + + assert.Nil(t, adapter.GetConnection()) +} diff --git a/pkg/srelib/launcher.go b/pkg/srelib/launcher.go new file mode 100644 index 00000000..0bede5fa --- /dev/null +++ b/pkg/srelib/launcher.go @@ -0,0 +1,60 @@ +package srelib + +import ( + "fmt" + "os/exec" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" + + "github.com/petrkotas/srelib/sdk" + v1 "github.com/petrkotas/srelib/sdk/v1" +) + +// PluginClient manages the lifecycle of the srelib plugin process. +type PluginClient struct { + raw *plugin.Client + client v1.Client +} + +// LaunchPlugin starts the srelib plugin binary and returns a connected client. +// The caller must call Close() when done. +func LaunchPlugin(pluginPath string, logger hclog.Logger) (*PluginClient, error) { + raw := plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: sdk.HandshakeConfig, + VersionedPlugins: map[int]plugin.PluginSet{1: {"srelib": &v1.Plugin{}}}, + Cmd: exec.Command(pluginPath), + Logger: logger, + AllowedProtocols: []plugin.Protocol{plugin.ProtocolNetRPC}, + }) + + rpcClient, err := raw.Client() + if err != nil { + raw.Kill() + return nil, fmt.Errorf("failed to connect to srelib plugin: %w", err) + } + + iface, err := rpcClient.Dispense("srelib") + if err != nil { + raw.Kill() + return nil, fmt.Errorf("failed to dispense srelib client: %w", err) + } + + client, ok := iface.(v1.Client) + if !ok { + raw.Kill() + return nil, fmt.Errorf("unexpected type from srelib plugin: %T", iface) + } + + return &PluginClient{raw: raw, client: client}, nil +} + +// Client returns the underlying srelib v1.Client. +func (p *PluginClient) Client() v1.Client { + return p.client +} + +// Close kills the plugin process. +func (p *PluginClient) Close() { + p.raw.Kill() +} diff --git a/poc/srelib-integration/main.go b/poc/srelib-integration/main.go new file mode 100644 index 00000000..f9585b0f --- /dev/null +++ b/poc/srelib-integration/main.go @@ -0,0 +1,76 @@ +// POC: Using srelib inside configuration-anomaly-detection. +// +// This demonstrates how to: +// 1. Launch the srelib plugin process +// 2. Wrap its client behind CAD's ocm.Client interface via the Adapter +// 3. Use it in CAD's investigation resource builder +// +// To run for real you need the srelib plugin binary built: +// +// cd ../srelib && go build -o srelib-plugin ./cmd/plugin +// SRELIB_PLUGIN_PATH=../srelib/srelib-plugin go run ./poc/srelib-integration +package main + +import ( + "fmt" + "os" + + "github.com/hashicorp/go-hclog" + + cadocm "github.com/openshift/configuration-anomaly-detection/pkg/ocm" + "github.com/openshift/configuration-anomaly-detection/pkg/srelib" +) + +func main() { + pluginPath := os.Getenv("SRELIB_PLUGIN_PATH") + if pluginPath == "" { + pluginPath = "srelib-plugin" + } + + logger := hclog.New(&hclog.LoggerOptions{ + Name: "cad-srelib-poc", + Level: hclog.Info, + }) + + fmt.Println("=== CAD + srelib POC ===") + fmt.Printf("Launching srelib plugin from: %s\n", pluginPath) + + pc, err := srelib.LaunchPlugin(pluginPath, logger) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to launch plugin: %v\n", err) + fmt.Println("\nTo build the plugin binary:") + fmt.Println(" cd ../srelib && go build -o srelib-plugin ./cmd/plugin") + os.Exit(1) + } + defer pc.Close() + + // Wrap the srelib client behind CAD's ocm.Client interface. + var ocmClient cadocm.Client = srelib.NewAdapter(pc.Client()) + + // Now use it exactly as CAD would — e.g. fetch cluster info. + clusterID := os.Getenv("CLUSTER_ID") + if clusterID == "" { + clusterID = "my-test-cluster" + } + + fmt.Printf("\nLooking up cluster: %s\n", clusterID) + cluster, err := ocmClient.GetClusterInfo(clusterID) + if err != nil { + fmt.Fprintf(os.Stderr, "GetClusterInfo failed: %v\n", err) + os.Exit(1) + } + + fmt.Printf("Cluster ID: %s\n", cluster.ID()) + fmt.Printf("Cluster Name: %s\n", cluster.Name()) + fmt.Printf("State: %s\n", cluster.State()) + + // Demonstrate getting the support role ARN via the same interface. + arn, err := ocmClient.GetSupportRoleARN(cluster.ID()) + if err != nil { + fmt.Fprintf(os.Stderr, "GetSupportRoleARN failed: %v\n", err) + } else { + fmt.Printf("Support ARN: %s\n", arn) + } + + fmt.Println("\n=== POC complete ===") +}