Skip to content

Commit 6fe1328

Browse files
SREP-1881 add oc to cad (#620)
* add OC cli to CAD image and adjust cpu and memory requests * SREP-1881: add oc client wrapper to allow usage of oc in CAD
1 parent 3150f05 commit 6fe1328

File tree

9 files changed

+423
-133
lines changed

9 files changed

+423
-133
lines changed

build/Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ ARG VERSION
1717
ARG VCS_REF
1818
ARG DOCKERFILE_PATH
1919

20+
RUN microdnf update -y && \
21+
microdnf install wget tar gzip -y
22+
RUN wget https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest/linux/oc.tar.gz -O /tmp/oc.tar.gz && \
23+
tar -zxvf /tmp/oc.tar.gz -C /tmp && \
24+
mv /tmp/oc /bin/oc && \
25+
rm -f /tmp/oc.tar.gz
26+
2027
LABEL vendor="RedHat" \
2128
name="openshift/configuration-anomaly-detection" \
2229
description="a CLI tool to detect and mitigate configuration mishaps" \

cadctl/cmd/investigate/investigate.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"github.com/openshift/configuration-anomaly-detection/pkg/investigations/ccam"
2929
"github.com/openshift/configuration-anomaly-detection/pkg/investigations/investigation"
3030
"github.com/openshift/configuration-anomaly-detection/pkg/investigations/precheck"
31-
k8sclient "github.com/openshift/configuration-anomaly-detection/pkg/k8s"
3231
"github.com/openshift/configuration-anomaly-detection/pkg/logging"
3332
"github.com/openshift/configuration-anomaly-detection/pkg/managedcloud"
3433
"github.com/openshift/configuration-anomaly-detection/pkg/metrics"
@@ -75,9 +74,6 @@ func run(_ *cobra.Command, _ []string) error {
7574
return fmt.Errorf("missing required environment variable BACKPLANE_URL")
7675
}
7776

78-
// Set k8s environment configuration for this session
79-
k8sclient.SetBackplaneURL(backplaneURL)
80-
8177
// Load managedcloud environment variables
8278
backplaneInitialARN := os.Getenv("BACKPLANE_INITIAL_ARN")
8379
if backplaneInitialARN == "" {
@@ -154,7 +150,7 @@ func run(_ *cobra.Command, _ []string) error {
154150
return fmt.Errorf("could not initialize backplane client: %w", err)
155151
}
156152

157-
builder, err := investigation.NewResourceBuilder(pdClient, ocmClient, bpClient, clusterID, alertInvestigation.Name(), logLevelFlag, pipelineNameEnv)
153+
builder, err := investigation.NewResourceBuilder(pdClient, ocmClient, bpClient, clusterID, alertInvestigation.Name(), logLevelFlag, pipelineNameEnv, backplaneURL)
158154
if err != nil {
159155
return fmt.Errorf("failed to create resource builder: %w", err)
160156
}
@@ -164,13 +160,21 @@ func run(_ *cobra.Command, _ []string) error {
164160
// We ignore the error here because we just want to get any resources that were created.
165161
resources, _ := builder.Build()
166162

167-
// Cleanup k8s client if it exists
168-
if resources != nil && resources.K8sClient != nil {
169-
// Failing the k8sclient cleanup call is not critical
163+
// Cleanup rest config if it exists
164+
if resources != nil && resources.RestConfig != nil {
165+
// Failing the rest config cleanup call is not critical
170166
// There is garbage collection for the RBAC within MCC https://issues.redhat.com/browse/OSD-27692
171167
// We only log the error for now but could add it to the investigation notes or handle differently
172-
logging.Info("Cleaning kube-api access")
173-
deferErr := resources.K8sClient.Clean()
168+
logging.Info("Cleaning cluster api access")
169+
deferErr := resources.RestConfig.Clean()
170+
if deferErr != nil {
171+
logging.Error(deferErr)
172+
}
173+
}
174+
175+
if resources != nil && resources.OCClient != nil {
176+
logging.Info("Cleaning oc kubeconfig file access")
177+
deferErr := resources.OCClient.Clean()
174178
if deferErr != nil {
175179
logging.Error(deferErr)
176180
}

openshift/template.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ objects:
339339
cpu: 100m
340340
memory: 256Mi
341341
requests:
342-
cpu: 10m
343-
memory: 64Mi
342+
cpu: 20m
343+
memory: 128Mi
344344
- apiVersion: batch/v1
345345
kind: CronJob
346346
metadata:

pkg/investigations/investigation/errors.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,40 @@ type AWSClientError struct {
2727
Err error
2828
}
2929

30-
func (a *AWSClientError) Unwrap() error { return a.Err }
30+
func (e AWSClientError) Unwrap() error { return e.Err }
3131

3232
func (e AWSClientError) Error() string {
3333
return fmt.Sprintf("could not retrieve aws credentials for %s: %s", e.ClusterID, e.Err.Error())
3434
}
3535

36+
type RestConfigError struct {
37+
ClusterID string
38+
Err error
39+
}
40+
41+
func (e RestConfigError) Unwrap() error { return e.Err }
42+
43+
func (e RestConfigError) Error() string {
44+
return fmt.Sprintf("could not create rest config for %s: %s", e.ClusterID, e.Err.Error())
45+
}
46+
47+
type OCClientError struct {
48+
ClusterID string
49+
Err error
50+
}
51+
52+
func (e OCClientError) Unwrap() error { return e.Err }
53+
54+
func (e OCClientError) Error() string {
55+
return fmt.Sprintf("could not create oc client for %s: %s", e.ClusterID, e.Err.Error())
56+
}
57+
3658
type K8SClientError struct {
3759
ClusterID string
3860
Err error
3961
}
4062

41-
func (a *K8SClientError) Unwrap() error { return a.Err }
63+
func (e K8SClientError) Unwrap() error { return e.Err }
4264

4365
func (e K8SClientError) Error() string {
4466
return fmt.Sprintf("could not build k8s client for %s: %s", e.ClusterID, e.Err.Error())

pkg/investigations/investigation/investigation.go

Lines changed: 125 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package investigation
22

33
import (
4+
"context"
5+
46
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
5-
"github.com/openshift/configuration-anomaly-detection/pkg/backplane"
7+
"github.com/openshift/backplane-cli/pkg/cli/config"
8+
bpremediation "github.com/openshift/backplane-cli/pkg/remediation"
9+
610
hivev1 "github.com/openshift/hive/apis/hive/v1"
11+
"k8s.io/client-go/rest"
712

813
"github.com/openshift/configuration-anomaly-detection/pkg/aws"
14+
"github.com/openshift/configuration-anomaly-detection/pkg/backplane"
915
k8sclient "github.com/openshift/configuration-anomaly-detection/pkg/k8s"
1016
"github.com/openshift/configuration-anomaly-detection/pkg/logging"
1117
"github.com/openshift/configuration-anomaly-detection/pkg/managedcloud"
1218
"github.com/openshift/configuration-anomaly-detection/pkg/notewriter"
19+
"github.com/openshift/configuration-anomaly-detection/pkg/oc"
1320
"github.com/openshift/configuration-anomaly-detection/pkg/ocm"
1421
"github.com/openshift/configuration-anomaly-detection/pkg/pagerduty"
1522
)
@@ -37,6 +44,7 @@ func NewResourceBuilder(
3744
name string,
3845
logLevel string,
3946
pipelineName string,
47+
backplaneUrl string,
4048
) (ResourceBuilder, error) {
4149
rb := &ResourceBuilderT{
4250
buildLogger: true,
@@ -45,6 +53,7 @@ func NewResourceBuilder(
4553
logLevel: logLevel,
4654
pipelineName: pipelineName,
4755
ocmClient: ocmClient,
56+
backplaneUrl: backplaneUrl,
4857
builtResources: &Resources{
4958
BpClient: bpClient,
5059
PdClient: pdClient,
@@ -72,17 +81,21 @@ type Resources struct {
7281
ClusterDeployment *hivev1.ClusterDeployment
7382
AwsClient aws.Client
7483
BpClient backplane.Client
84+
RestConfig *RestConfig
7585
K8sClient k8sclient.Client
7686
OcmClient ocm.Client
7787
PdClient pagerduty.Client
7888
Notes *notewriter.NoteWriter
89+
OCClient oc.Client
7990
}
8091

8192
type ResourceBuilder interface {
8293
WithCluster() ResourceBuilder
8394
WithClusterDeployment() ResourceBuilder
8495
WithAwsClient() ResourceBuilder
96+
WithRestConfig() ResourceBuilder
8597
WithK8sClient() ResourceBuilder
98+
WithOC() ResourceBuilder
8699
WithNotes() ResourceBuilder
87100
Build() (*Resources, error)
88101
}
@@ -91,14 +104,17 @@ type ResourceBuilderT struct {
91104
buildCluster bool
92105
buildClusterDeployment bool
93106
buildAwsClient bool
107+
buildRestConfig bool
94108
buildK8sClient bool
109+
buildOC bool
95110
buildNotes bool
96111
buildLogger bool
97112

98113
clusterId string
99114
name string
100115
logLevel string
101116
pipelineName string
117+
backplaneUrl string
102118

103119
ocmClient *ocm.SdkClient
104120

@@ -118,13 +134,26 @@ func (r *ResourceBuilderT) WithClusterDeployment() ResourceBuilder {
118134
return r
119135
}
120136

137+
func (r *ResourceBuilderT) WithRestConfig() ResourceBuilder {
138+
r.WithCluster()
139+
r.buildRestConfig = true
140+
return r
141+
}
142+
121143
func (r *ResourceBuilderT) WithAwsClient() ResourceBuilder {
122144
r.WithCluster()
123145
r.buildAwsClient = true
124146
return r
125147
}
126148

149+
func (r *ResourceBuilderT) WithOC() ResourceBuilder {
150+
r.WithRestConfig()
151+
r.buildOC = true
152+
return r
153+
}
154+
127155
func (r *ResourceBuilderT) WithK8sClient() ResourceBuilder {
156+
r.WithRestConfig()
128157
r.buildK8sClient = true
129158
return r
130159
}
@@ -154,49 +183,107 @@ func (r *ResourceBuilderT) Build() (*Resources, error) {
154183
}
155184
}
156185

157-
// Dependent resources can only be built if a cluster object exists.
158-
//nolint:nestif
159-
if r.builtResources.Cluster != nil {
160-
internalClusterId := r.builtResources.Cluster.ID()
161-
162-
if r.buildAwsClient && r.builtResources.AwsClient == nil {
163-
r.builtResources.AwsClient, err = managedcloud.CreateCustomerAWSClient(r.builtResources.Cluster, r.ocmClient)
164-
if err != nil {
165-
r.buildErr = AWSClientError{ClusterID: r.clusterId, Err: err}
166-
return r.builtResources, r.buildErr
167-
}
186+
if r.buildNotes && r.builtResources.Notes == nil {
187+
r.builtResources.Notes = notewriter.New(r.name, logging.RawLogger)
188+
}
189+
190+
internalClusterId := r.builtResources.Cluster.ID()
191+
192+
if r.buildAwsClient && r.builtResources.AwsClient == nil {
193+
r.builtResources.AwsClient, err = managedcloud.CreateCustomerAWSClient(r.builtResources.Cluster, r.ocmClient)
194+
if err != nil {
195+
r.buildErr = AWSClientError{ClusterID: r.clusterId, Err: err}
196+
return r.builtResources, r.buildErr
168197
}
198+
}
169199

170-
if r.buildK8sClient && r.builtResources.K8sClient == nil {
171-
logging.Infof("creating k8s client for %s", r.name)
172-
r.builtResources.K8sClient, err = k8sclient.New(r.builtResources.Cluster.ID(), r.ocmClient, r.name)
173-
if err != nil {
174-
r.buildErr = K8SClientError{ClusterID: r.clusterId, Err: err}
175-
return r.builtResources, r.buildErr
176-
}
200+
if r.buildRestConfig && r.builtResources.RestConfig == nil {
201+
r.builtResources.RestConfig, err = newRestConfig(r.builtResources.Cluster.ID(), r.backplaneUrl, r.ocmClient, r.name)
202+
if err != nil {
203+
r.buildErr = RestConfigError{ClusterID: r.clusterId, Err: err}
204+
return r.builtResources, r.buildErr
177205
}
206+
}
207+
208+
if r.buildK8sClient && r.builtResources.K8sClient == nil {
209+
logging.Infof("creating k8s client for %s", r.name)
210+
r.builtResources.K8sClient, err = k8sclient.New(&r.builtResources.RestConfig.Config)
211+
if err != nil {
212+
r.buildErr = K8SClientError{ClusterID: r.clusterId, Err: err}
213+
return r.builtResources, r.buildErr
214+
}
215+
}
178216

179-
if r.buildClusterDeployment && r.builtResources.ClusterDeployment == nil {
180-
r.builtResources.ClusterDeployment, err = r.ocmClient.GetClusterDeployment(internalClusterId)
181-
if err != nil {
182-
r.buildErr = ClusterDeploymentNotFoundError{ClusterID: r.clusterId, Err: err}
183-
return r.builtResources, r.buildErr
184-
}
217+
if r.buildOC && r.builtResources.OCClient == nil {
218+
r.builtResources.OCClient, err = oc.New(context.Background(), &r.builtResources.RestConfig.Config)
219+
if err != nil {
220+
r.buildErr = OCClientError{ClusterID: r.clusterId, Err: err}
221+
return r.builtResources, r.buildErr
185222
}
223+
}
186224

187-
if r.buildLogger {
188-
// Re-initialize the logger with the cluster ID.
189-
logging.RawLogger = logging.InitLogger(r.logLevel, r.pipelineName, internalClusterId)
225+
if r.buildClusterDeployment && r.builtResources.ClusterDeployment == nil {
226+
r.builtResources.ClusterDeployment, err = r.ocmClient.GetClusterDeployment(internalClusterId)
227+
if err != nil {
228+
r.buildErr = ClusterDeploymentNotFoundError{ClusterID: r.clusterId, Err: err}
229+
return r.builtResources, r.buildErr
190230
}
191231
}
192232

193-
if r.buildNotes && r.builtResources.Notes == nil {
194-
r.builtResources.Notes = notewriter.New(r.name, logging.RawLogger)
233+
if r.buildLogger {
234+
// Re-initialize the logger with the cluster ID.
235+
logging.RawLogger = logging.InitLogger(r.logLevel, r.pipelineName, internalClusterId)
195236
}
196237

197238
return r.builtResources, nil
198239
}
199240

241+
type remediationCleaner struct {
242+
clusterID string
243+
ocmClient ocm.Client
244+
remediationInstanceId string
245+
backplaneUrl string
246+
}
247+
248+
type Cleaner interface {
249+
Clean() error
250+
}
251+
252+
type RestConfig struct {
253+
rest.Config
254+
backplaneUrl string
255+
Cleaner
256+
}
257+
258+
func (cleaner remediationCleaner) Clean() error {
259+
return deleteRemediation(cleaner.clusterID, cleaner.backplaneUrl, cleaner.ocmClient, cleaner.remediationInstanceId)
260+
}
261+
262+
// New returns a k8s rest config for the given cluster scoped to a given remediation's permissions.
263+
func newRestConfig(clusterID, backplaneUrl string, ocmClient ocm.Client, remediationName string) (*RestConfig, error) {
264+
decoratedCfg, remediationInstanceId, err := bpremediation.CreateRemediationWithConn(
265+
config.BackplaneConfiguration{URL: backplaneUrl},
266+
ocmClient.GetConnection(),
267+
clusterID,
268+
remediationName,
269+
)
270+
if err != nil {
271+
return nil, err
272+
}
273+
274+
return &RestConfig{*decoratedCfg, backplaneUrl, remediationCleaner{clusterID, ocmClient, remediationInstanceId, backplaneUrl}}, nil
275+
}
276+
277+
// Cleanup removes the remediation created for the cluster.
278+
func deleteRemediation(clusterID, backplaneUrl string, ocmClient ocm.Client, remediationInstanceId string) error {
279+
return bpremediation.DeleteRemediationWithConn(
280+
config.BackplaneConfiguration{URL: backplaneUrl},
281+
ocmClient.GetConnection(),
282+
clusterID,
283+
remediationInstanceId,
284+
)
285+
}
286+
200287
// This is an implementation to be used in tests, but putting it into a _test.go file will make it not resolvable.
201288
type ResourceBuilderMock struct {
202289
Resources *Resources
@@ -215,6 +302,14 @@ func (r *ResourceBuilderMock) WithAwsClient() ResourceBuilder {
215302
return r
216303
}
217304

305+
func (r *ResourceBuilderMock) WithRestConfig() ResourceBuilder {
306+
return r
307+
}
308+
309+
func (r *ResourceBuilderMock) WithOC() ResourceBuilder {
310+
return r
311+
}
312+
218313
func (r *ResourceBuilderMock) WithNotes() ResourceBuilder {
219314
return r
220315
}

0 commit comments

Comments
 (0)