-
Notifications
You must be signed in to change notification settings - Fork 31
[FSE-1855] Read Flink statement warnings from status.warnings #3419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,17 +8,34 @@ import ( | |
| cmfsdk "github.com/confluentinc/cmf-sdk-go/v1" | ||
|
|
||
| "github.com/confluentinc/cli/v4/pkg/config" | ||
| "github.com/confluentinc/cli/v4/pkg/flink/types" | ||
| "github.com/confluentinc/cli/v4/pkg/output" | ||
| ) | ||
|
|
||
| // printStatementWarnings renders warnings below the table, on stderr so that stdout stays the | ||
| // command's data. Serialized output already carries them in the warnings field. | ||
| func printStatementWarnings(cmd *cobra.Command, warnings []types.StatementWarning) { | ||
| if output.GetFormat(cmd) != output.Human { | ||
| return | ||
| } | ||
|
|
||
| if block := types.FormatStatementWarnings(warnings); block != "" { | ||
| output.ErrPrintln(false, "") | ||
| output.ErrPrintln(false, block) | ||
| output.ErrPrintln(false, "") | ||
| } | ||
|
Comment on lines
+18
to
+26
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it work for the ticket to log these to It's only in the human readable output so it's not necessarily an issue, but it does deviate from the usual pattern for CLI outputs. If we do want these to always be displayed, let's write it to stderr instead.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point on the convention, moved to stderr for the non-shell commands. |
||
| } | ||
|
|
||
| type statementOut struct { | ||
| CreationDate time.Time `human:"Creation Date" serialized:"creation_date"` | ||
| Name string `human:"Name" serialized:"name"` | ||
| Statement string `human:"Statement" serialized:"statement"` | ||
| ComputePool string `human:"Compute Pool,omitempty" serialized:"compute_pool,omitempty"` | ||
| Status string `human:"Status" serialized:"status"` | ||
| StatusDetail string `human:"Status Detail,omitempty" serialized:"status_detail,omitempty"` | ||
| LatestOffsets map[string]string `human:"Latest Offsets" serialized:"latest_offsets"` | ||
| LatestOffsetsTimestamp *time.Time `human:"Latest Offsets Timestamp" serialized:"latest_offsets_timestamp"` | ||
| CreationDate time.Time `human:"Creation Date" serialized:"creation_date"` | ||
| Name string `human:"Name" serialized:"name"` | ||
| Statement string `human:"Statement" serialized:"statement"` | ||
| ComputePool string `human:"Compute Pool,omitempty" serialized:"compute_pool,omitempty"` | ||
| Status string `human:"Status" serialized:"status"` | ||
| StatusDetail string `human:"Status Detail,omitempty" serialized:"status_detail,omitempty"` | ||
| Warnings []types.StatementWarning `human:"-" serialized:"warnings,omitempty"` | ||
| LatestOffsets map[string]string `human:"Latest Offsets" serialized:"latest_offsets"` | ||
| LatestOffsetsTimestamp *time.Time `human:"Latest Offsets Timestamp" serialized:"latest_offsets_timestamp"` | ||
| } | ||
|
|
||
| type statementOutOnPrem struct { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,7 +54,7 @@ | |
| return cmd | ||
| } | ||
|
|
||
| func (c *command) statementCreate(cmd *cobra.Command, args []string) error { | ||
|
Check failure on line 57 in internal/flink/command_statement_create.go
|
||
| environmentId, err := c.Context.EnvironmentId() | ||
| if err != nil { | ||
| return err | ||
|
|
@@ -173,6 +173,8 @@ | |
| } | ||
| } | ||
|
|
||
| warnings := types.NewStatementWarnings(statement.Status.GetWarnings()) | ||
|
raminqaf marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line can be merged with line#186, but don't worry, I will make this change. |
||
|
|
||
| table := output.NewTable(cmd) | ||
| table.Add(&statementOut{ | ||
| CreationDate: statement.Metadata.GetCreatedAt(), | ||
|
|
@@ -181,7 +183,13 @@ | |
| ComputePool: statement.Spec.GetComputePoolId(), | ||
| Status: statement.Status.GetPhase(), | ||
| StatusDetail: statement.Status.GetDetail(), | ||
| Warnings: warnings, | ||
| }) | ||
| table.Filter([]string{"CreationDate", "Name", "Statement", "ComputePool", "Status", "StatusDetail"}) | ||
| return table.Print() | ||
| table.Filter([]string{"CreationDate", "Name", "Statement", "ComputePool", "Status", "StatusDetail", "Warnings"}) | ||
| if err := table.Print(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| printStatementWarnings(cmd, warnings) | ||
|
channingdong marked this conversation as resolved.
|
||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,20 +8,22 @@ import ( | |
| flinkgatewayv1 "github.com/confluentinc/ccloud-sdk-go-v2/flink-gateway/v1" | ||
|
|
||
| pcmd "github.com/confluentinc/cli/v4/pkg/cmd" | ||
| "github.com/confluentinc/cli/v4/pkg/flink/types" | ||
| "github.com/confluentinc/cli/v4/pkg/output" | ||
| ) | ||
|
|
||
| type describeStatementOut struct { | ||
| CreationDate time.Time `human:"Creation Date" serialized:"creation_date"` | ||
| Name string `human:"Name" serialized:"name"` | ||
| Statement string `human:"Statement" serialized:"statement"` | ||
| ComputePool string `human:"Compute Pool" serialized:"compute_pool"` | ||
| Status string `human:"Status" serialized:"status"` | ||
| StatusDetail string `human:"Status Detail,omitempty" serialized:"status_detail,omitempty"` | ||
| LatestOffsets map[string]string `human:"Latest Offsets" serialized:"latest_offsets"` | ||
| LatestOffsetsTimestamp *time.Time `human:"Latest Offsets Timestamp" serialized:"latest_offsets_timestamp"` | ||
| Properties map[string]string `human:"Properties" serialized:"properties"` | ||
| Principal string `human:"Principal" serialized:"principal"` | ||
| CreationDate time.Time `human:"Creation Date" serialized:"creation_date"` | ||
| Name string `human:"Name" serialized:"name"` | ||
| Statement string `human:"Statement" serialized:"statement"` | ||
| ComputePool string `human:"Compute Pool" serialized:"compute_pool"` | ||
| Status string `human:"Status" serialized:"status"` | ||
| StatusDetail string `human:"Status Detail,omitempty" serialized:"status_detail,omitempty"` | ||
| Warnings []types.StatementWarning `human:"-" serialized:"warnings,omitempty"` | ||
| LatestOffsets map[string]string `human:"Latest Offsets" serialized:"latest_offsets"` | ||
| LatestOffsetsTimestamp *time.Time `human:"Latest Offsets Timestamp" serialized:"latest_offsets_timestamp"` | ||
| Properties map[string]string `human:"Properties" serialized:"properties"` | ||
| Principal string `human:"Principal" serialized:"principal"` | ||
| } | ||
|
|
||
| func (c *command) newStatementDescribeCommand() *cobra.Command { | ||
|
|
@@ -58,6 +60,8 @@ func (c *command) statementDescribe(cmd *cobra.Command, args []string) error { | |
| return err | ||
| } | ||
|
|
||
| warnings := types.NewStatementWarnings(statement.Status.GetWarnings()) | ||
|
|
||
|
raminqaf marked this conversation as resolved.
|
||
| table := output.NewTable(cmd) | ||
| table.Add(&describeStatementOut{ | ||
| CreationDate: statement.Metadata.GetCreatedAt(), | ||
|
|
@@ -66,10 +70,16 @@ func (c *command) statementDescribe(cmd *cobra.Command, args []string) error { | |
| ComputePool: statement.Spec.GetComputePoolId(), | ||
| Status: statement.Status.GetPhase(), | ||
| StatusDetail: statement.Status.GetDetail(), | ||
| Warnings: warnings, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This means having the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are never both shown. |
||
| LatestOffsets: statement.Status.GetLatestOffsets(), | ||
| LatestOffsetsTimestamp: flinkgatewayv1.PtrTime(statement.Status.GetLatestOffsetsTimestamp()), | ||
| Properties: statement.Spec.GetProperties(), | ||
| Principal: statement.Spec.GetPrincipal(), | ||
| }) | ||
| return table.Print() | ||
| if err := table.Print(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| printStatementWarnings(cmd, warnings) | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import ( | |
| pcmd "github.com/confluentinc/cli/v4/pkg/cmd" | ||
| "github.com/confluentinc/cli/v4/pkg/errors" | ||
| "github.com/confluentinc/cli/v4/pkg/examples" | ||
| "github.com/confluentinc/cli/v4/pkg/flink/types" | ||
| "github.com/confluentinc/cli/v4/pkg/log" | ||
| "github.com/confluentinc/cli/v4/pkg/output" | ||
| "github.com/confluentinc/cli/v4/pkg/utils" | ||
|
|
@@ -100,6 +101,7 @@ func (c *command) statementList(cmd *cobra.Command, _ []string) error { | |
| ComputePool: statement.Spec.GetComputePoolId(), | ||
| Status: statement.Status.GetPhase(), | ||
| StatusDetail: statement.Status.GetDetail(), | ||
| Warnings: types.NewStatementWarnings(statement.Status.GetWarnings()), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with limiting the full block to One thing worth confirming: If you want |
||
| LatestOffsets: statement.Status.GetLatestOffsets(), | ||
| LatestOffsetsTimestamp: flinkgatewayv1.PtrTime(statement.Status.GetLatestOffsetsTimestamp()), | ||
| }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| Statement successfully submitted. | ||
| Waiting for statement to be ready. Statement phase: PENDING. | ||
| Warnings: | ||
|
|
||
| CRITICAL [UPSERT_PRIMARY_KEY_MISMATCH] (Logged: 2026-07-30T09:15:00Z) | ||
| The primary key does not match the upsert key derived from the query. | ||
|
|
||
| MODERATE [MISSING_WINDOW_START_END] (Logged: 2026-07-30T08:00:00Z) | ||
| The GROUP BY clause contains only `window_start` with no corresponding `window_end`. | ||
|
|
||
| Statement phase is RUNNING. | ||
| Listening for execution errors. Press Enter to detach. | ||
| Finished statement execution. Statement phase: COMPLETED. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| package types | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
|
|
||
| testUtils "github.com/confluentinc/cli/v4/pkg/flink/test" | ||
| ) | ||
|
|
||
| func TestPrintStatusMessagePrintsStructuredWarningsInsteadOfStatusDetail(t *testing.T) { | ||
| statement := ProcessedStatement{ | ||
| Status: RUNNING, | ||
| StatusDetail: "[Warning] legacy inlined warning", | ||
| Warnings: []StatementWarning{{Severity: "CRITICAL", Reason: "SOME_REASON", Message: "Fix the query."}}, | ||
| } | ||
|
|
||
| stdout := testUtils.RunAndCaptureSTDOUT(t, statement.PrintStatusMessage) | ||
|
|
||
| require.Contains(t, stdout, "CRITICAL [SOME_REASON]") | ||
| require.Contains(t, stdout, "Fix the query.") | ||
| require.NotContains(t, stdout, "legacy inlined warning") | ||
| require.NotContains(t, stdout, "Details: ") | ||
| } | ||
|
|
||
| func TestPrintStatusMessagePrintsStatusDetailOfFailedStatementAlongsideWarnings(t *testing.T) { | ||
| statement := ProcessedStatement{ | ||
| Status: FAILED, | ||
| StatusDetail: "the failure reason", | ||
| Warnings: []StatementWarning{{Severity: "LOW", Reason: "SOME_REASON", Message: "Fix the query."}}, | ||
| } | ||
|
|
||
| stdout := testUtils.RunAndCaptureSTDOUT(t, statement.PrintStatusMessage) | ||
|
|
||
| require.Contains(t, stdout, "the failure reason") | ||
| require.Contains(t, stdout, "LOW [SOME_REASON]") | ||
| } | ||
|
|
||
| func TestPrintStatusMessagePrintsStatusDetailWhenThereAreNoWarnings(t *testing.T) { | ||
| statement := ProcessedStatement{Status: RUNNING, StatusDetail: "something worth knowing"} | ||
|
|
||
| stdout := testUtils.RunAndCaptureSTDOUT(t, statement.PrintStatusMessage) | ||
|
|
||
| require.Contains(t, stdout, "Details: ") | ||
| require.Contains(t, stdout, "something worth knowing") | ||
| require.NotContains(t, stdout, "Warnings:") | ||
| } | ||
|
|
||
| func TestPrintStatusMessagePrintsNoWarningsBlockWhenThereAreNoWarnings(t *testing.T) { | ||
| statement := ProcessedStatement{Status: RUNNING} | ||
|
|
||
| stdout := testUtils.RunAndCaptureSTDOUT(t, statement.PrintStatusMessage) | ||
|
|
||
| require.Contains(t, stdout, "Statement successfully submitted.") | ||
| require.NotContains(t, stdout, "Warnings:") | ||
| require.NotContains(t, stdout, "Details: ") | ||
| } | ||
|
|
||
| func TestPrintOutputDryRunStatementPrintsWarnings(t *testing.T) { | ||
| statement := ProcessedStatement{ | ||
| Status: COMPLETED, | ||
| Warnings: []StatementWarning{{Severity: "MODERATE", Reason: "SOME_REASON", Message: "Fix the query."}}, | ||
| } | ||
|
|
||
| stdout := testUtils.RunAndCaptureSTDOUT(t, statement.PrintOutputDryRunStatement) | ||
|
|
||
| require.Contains(t, stdout, "MODERATE [SOME_REASON]") | ||
| require.Contains(t, stdout, "Fix the query.") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's ok to leave the dedicated human output function as is, but I may move it to a dedicated utils file so that we can automate all future CLI changes.