@@ -3,10 +3,13 @@ package cad
33import (
44 "context"
55 "fmt"
6+ "slices"
67
8+ "github.com/openshift/osdctl/cmd/setup"
79 "github.com/openshift/osdctl/pkg/k8s"
810 "github.com/openshift/osdctl/pkg/utils"
911 "github.com/spf13/cobra"
12+ "github.com/spf13/viper"
1013 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1114 "k8s.io/apimachinery/pkg/runtime/schema"
1215 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -15,6 +18,8 @@ import (
1518const (
1619 cadClusterIDProd = "2fbi9mjhqpobh20ot5d7e5eeq3a8gfhs" // These IDs are hard-coded in app-interface
1720 cadClusterIDStage = "2f9ghpikkv446iidcv7b92em2hgk13q9"
21+ cadNamespaceProd = "configuration-anomaly-detection-production"
22+ cadNamespaceStage = "configuration-anomaly-detection-stage"
1823)
1924
2025var validInvestigations = []string {
@@ -40,6 +45,7 @@ type cadRunOptions struct {
4045 investigation string
4146 elevationReason string
4247 environment string
48+ isDryRun bool
4349}
4450
4551func newCmdRun () * cobra.Command {
@@ -51,7 +57,7 @@ func newCmdRun() *cobra.Command {
5157 Long : `Run a manual investigation on the Configuration Anomaly Detection (CAD) cluster.
5258
5359This command schedules a Tekton PipelineRun on the appropriate CAD cluster (stage or production)
54- to run an investigation against a target cluster.
60+ to run an investigation against a target cluster. The results will be written to a backplane report.
5561
5662Prerequisites:
5763 - Connected to the target cluster's OCM environment (production or stage)
@@ -61,17 +67,29 @@ Available Investigations:
6167 chgm, cmbb, can-not-retrieve-updates, ai, cpd, etcd-quota-low,
6268 insightsoperatordown, machine-health-check, must-gather, upgrade-config
6369
64- Example:
65- # Run a change management investigation on a production cluster
66- osdctl cluster cad run \
67- --cluster-id 1a2b3c4d5e6f7g8h9i0j \
68- --investigation chgm \
69- --environment production \
70- --reason "OHSS-12345"
70+ Examples:
71+ ` + "```bash" + `
72+ # Run a change management investigation on a production cluster
73+ osdctl cluster cad run \
74+ --cluster-id 1a2b3c4d5e6f7g8h9i0j \
75+ --investigation chgm \
76+ --environment production \
77+ --reason "OHSS-12345"
78+
79+ # Run a dry-run investigation (does not create a report)
80+ osdctl cluster cad run \
81+ --cluster-id 1a2b3c4d5e6f7g8h9i0j \
82+ --investigation chgm \
83+ --environment production \
84+ --reason "OHSS-12345" \
85+ --dry-run
86+ ` + "```" + `
7187
7288Note:
7389 After the investigation completes (may take several minutes), view results using:
74- osdctl cluster reports list -C <cluster-id> -l 1
90+ ` + "```bash" + `
91+ osdctl cluster reports list -C <cluster-id> -l 1
92+ ` + "```" + `
7593
7694 You must be connected to the target cluster's OCM environment to view its reports.` ,
7795 Args : cobra .NoArgs ,
@@ -83,9 +101,15 @@ Note:
83101
84102 runCmd .Flags ().StringVarP (& opts .clusterID , "cluster-id" , "C" , "" , "Cluster ID (internal or external)" )
85103 runCmd .Flags ().StringVarP (& opts .investigation , "investigation" , "i" , "" , "Investigation name" )
86- runCmd .Flags ().StringVarP (& opts .environment , "environment" , "e" , "" , "Environment of the cluster we want to run the investigation on. Allowed values: \" stage\" or \" production\" " )
104+ runCmd .Flags ().StringVarP (& opts .environment , "environment" , "e" , "" , "Environment in which the target cluster runs. Allowed values: \" stage\" or \" production\" " )
105+ runCmd .Flags ().BoolVarP (& opts .isDryRun , "dry-run" , "d" , false , "Dry-Run: Run the investigation with the dry-run flag. This will not create a report." )
87106 runCmd .Flags ().StringVar (& opts .elevationReason , "reason" , "" , "Provide a reason for running a manual investigation, used for backplane. Eg: 'OHSS-XXXX', or '#ITN-2024-XXXXX." )
88107
108+ _ = runCmd .MarkFlagRequired ("cluster-id" )
109+ _ = runCmd .MarkFlagRequired ("investigation" )
110+ _ = runCmd .MarkFlagRequired ("environment" )
111+ _ = runCmd .MarkFlagRequired ("reason" )
112+
89113 _ = runCmd .RegisterFlagCompletionFunc ("investigation" , func (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
90114 return validInvestigations , cobra .ShellCompDirectiveNoFileComp
91115 })
@@ -102,6 +126,9 @@ func (o *cadRunOptions) run() error {
102126 return err
103127 }
104128
129+ grafanaURL := viper .GetString (setup .CADGrafanaURL )
130+ awsAccountID := viper .GetString (setup .CADAWSAccountID )
131+
105132 cadClusterID , cadNamespace := o .getCADClusterConfig ()
106133
107134 // CAD clusters are always in production OCM, so explicitly create a production connection
@@ -123,42 +150,46 @@ func (o *cadRunOptions) run() error {
123150 return fmt .Errorf ("failed to schedule task: %w" , err )
124151 }
125152
126- reportCmd := fmt .Sprintf ("'osdctl cluster reports list -C %s -l 1'" , o .clusterID )
127- fmt .Println ("Successfully scheduled manual investigation. It can take several minutes until a report is available. Run this command to check the latest report for the results while being connected to the right OCM backplane environment. " + reportCmd )
153+ // Get the generated name created by the API server
154+ pipelineRunName := u .GetName ()
155+
156+ var logsLink string
157+ if grafanaURL != "" && awsAccountID != "" {
158+ logsLink = fmt .Sprintf ("%s/explore?schemaVersion=1&panes=%%7B%%22buh%%22:%%7B%%22datasource%%22:%%22P1A97A9592CB7F392%%22,%%22queries%%22:%%5B%%7B%%22id%%22:%%22%%22,%%22region%%22:%%22us-east-1%%22,%%22namespace%%22:%%22%%22,%%22refId%%22:%%22A%%22,%%22datasource%%22:%%7B%%22type%%22:%%22cloudwatch%%22,%%22uid%%22:%%22P1A97A9592CB7F392%%22%%7D,%%22queryMode%%22:%%22Logs%%22,%%22logGroups%%22:%%5B%%7B%%22arn%%22:%%22arn:aws:logs:us-east-1:%[2]s:log-group:cads01ue1.configuration-anomaly-detection-stage:%%2A%%22,%%22name%%22:%%22cads01ue1.configuration-anomaly-detection-stage%%22,%%22accountId%%22:%%22%[2]s%%22%%7D,%%7B%%22arn%%22:%%22arn:aws:logs:us-east-1:%[2]s:log-group:cadp01ue1.configuration-anomaly-detection-production:%%2A%%22,%%22name%%22:%%22cadp01ue1.configuration-anomaly-detection-production%%22,%%22accountId%%22:%%22%[2]s%%22%%7D%%5D,%%22expression%%22:%%22fields%%20message%%5Cn%%7C%%20filter%%20kubernetes.pod_name%%20like%%20%%5C%%22%s%%5C%%22%%22,%%22statsGroups%%22:%%5B%%5D%%7D%%5D,%%22range%%22:%%7B%%22from%%22:%%22now-1h%%22,%%22to%%22:%%22now%%22%%7D,%%22panelsState%%22:%%7B%%22logs%%22:%%7B%%22visualisationType%%22:%%22logs%%22%%7D%%7D%%7D%%7D&orgId=1" , grafanaURL , awsAccountID , pipelineRunName )
159+ }
160+
161+ if ! o .isDryRun {
162+ reportCmd := fmt .Sprintf ("'osdctl cluster reports list -C %s -l 1'" , o .clusterID )
163+ msg := "Successfully scheduled manual investigation. It can take several minutes until a report is available. \n " +
164+ "Run this command to check the latest report for the results while being connected to the right OCM backplane environment. " + reportCmd + " \n "
165+
166+ if logsLink != "" {
167+ msg += "If a report fails to show up, check the TaskRun pod logs here after a few minutes: " + logsLink
168+ } else {
169+ msg += "To view TaskRun pod logs, configure 'cad_grafana_url' and 'cad_aws_account_id' using 'osdctl setup'"
170+ }
171+ fmt .Println (msg )
172+ } else {
173+ if logsLink != "" {
174+ fmt .Println ("Dry-run investigation scheduled. Check for logs here: " , logsLink )
175+ } else {
176+ fmt .Println ("Dry-run investigation scheduled. To view logs, configure 'cad_grafana_url' and 'cad_aws_account_id' using 'osdctl setup'" )
177+ }
178+ }
128179
129180 return nil
130181}
131182
132183func (o * cadRunOptions ) validate () error {
133- conn , err := utils .CreateConnection ()
134- if err != nil {
135- return err
136- }
137- defer conn .Close ()
138-
139184 if o .clusterID == "" {
140185 return fmt .Errorf ("cluster-id is required" )
141186 }
142187
143- validInvestigation := false
144- for _ , v := range validInvestigations {
145- if o .investigation == v {
146- validInvestigation = true
147- break
148- }
149- }
150- if ! validInvestigation {
188+ if ! slices .Contains (validInvestigations , o .investigation ) {
151189 return fmt .Errorf ("invalid investigation %q, must be one of: %v" , o .investigation , validInvestigations )
152190 }
153191
154- validEnvironment := false
155- for _ , v := range validEnvironments {
156- if o .environment == v {
157- validEnvironment = true
158- break
159- }
160- }
161- if ! validEnvironment {
192+ if ! slices .Contains (validEnvironments , o .environment ) {
162193 return fmt .Errorf ("invalid environment %q, must be one of: %v" , o .environment , validEnvironments )
163194 }
164195
@@ -171,9 +202,9 @@ func (o *cadRunOptions) validate() error {
171202
172203func (o * cadRunOptions ) getCADClusterConfig () (clusterID , namespace string ) {
173204 if o .environment == "stage" {
174- return cadClusterIDStage , "configuration-anomaly-detection-stage"
205+ return cadClusterIDStage , cadNamespaceStage
175206 }
176- return cadClusterIDProd , "configuration-anomaly-detection-production"
207+ return cadClusterIDProd , cadNamespaceProd
177208}
178209
179210func (o * cadRunOptions ) pipelineRunTemplate (cadNamespace string ) * unstructured.Unstructured {
@@ -197,7 +228,7 @@ func (o *cadRunOptions) pipelineRunTemplate(cadNamespace string) *unstructured.U
197228 },
198229 {
199230 "name" : "dry-run" ,
200- "value" : "false" ,
231+ "value" : o . isDryRun ,
201232 },
202233 },
203234 "pipelineRef" : map [string ]interface {}{
0 commit comments