-
Notifications
You must be signed in to change notification settings - Fork 30
[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 |
|---|---|---|
|
|
@@ -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.
|
||
|
|
||
| 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) | ||
|
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. Are the warnings immediately available after the create of the statement if we decide not to
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. Yes. |
||
| 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.
Would it work for the ticket to log these to
-v(warn), at least for the non-shell outputs?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.
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.
Good point on the convention, moved to stderr for the non-shell commands.
stdoutis now just the table, so redirecting or piping gives you clean data, and-o json/-o yamlare untouched since they carry the warnings in the payload itself.I left the shell on stdout. Everything there is already conversational output on stdout, including the status messages and the old
Details: banner, so splitting only the warnings onto stderr would be inconsistent with the surrounding lines.On
-v: I'd rather not hide them behind a verbosity flag. The requirement for this feature is that all warnings are always shown, which is the whole reason for moving them out ofstatus.detail, so making them opt-in would undo that.Goldens are unchanged since the integration harness captures combined output.