mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 01:03:40 +00:00
Add initial OSS part of license expiry code. (#16753)
In “tsh login” show only alerts with on-login label. In “tsh status” show only alerts with “high” severity. Which license warning should match. In all “tctl” commands show only alerts with “high” severity.
This commit is contained in:
parent
b289295f93
commit
d0a602deb6
|
@ -19,6 +19,7 @@ package constants
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
@ -347,3 +348,8 @@ const (
|
|||
// SupportedAzureDiscoveryServices is list of Azure services currently
|
||||
// supported by the Teleport discovery service.
|
||||
var SupportedAzureDiscoveryServices = []string{AzureServiceTypeKubernetes}
|
||||
|
||||
const (
|
||||
// TimeoutGetClusterAlerts is the timeout for grabbing cluster alerts from tctl and tsh
|
||||
TimeoutGetClusterAlerts = time.Millisecond * 500
|
||||
)
|
||||
|
|
|
@ -485,6 +485,9 @@ const (
|
|||
// a mechanism for reducing noise/redundancy, and is not a form of access control. Use
|
||||
// one of the "permit" labels if you need to restrict viewership of an alert.
|
||||
AlertSupersedes = "teleport.internal/alert-supersedes"
|
||||
|
||||
// AlertLicenseExpired is an internal label that indicates that the license has expired.
|
||||
AlertLicenseExpired = "teleport.internal/license-expired-warning"
|
||||
)
|
||||
|
||||
// RequestableResourceKinds lists all Teleport resource kinds users can request access to.
|
||||
|
|
2
e
2
e
|
@ -1 +1 @@
|
|||
Subproject commit 93a19fd5eda61fab3464f77aeabc50eea7594e8e
|
||||
Subproject commit aa71cbb7e14c9f1bda4115515815c3008294e65c
|
|
@ -39,6 +39,7 @@ import (
|
|||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
)
|
||||
|
||||
type LoggingPurpose int
|
||||
|
@ -522,3 +523,26 @@ type PredicateError struct {
|
|||
func (p PredicateError) Error() string {
|
||||
return fmt.Sprintf("%s\nCheck syntax at https://goteleport.com/docs/setup/reference/predicate-language/#resource-filtering", p.Err.Error())
|
||||
}
|
||||
|
||||
// FormatAlert formats and colors the alert message if possible.
|
||||
func FormatAlert(alert types.ClusterAlert) string {
|
||||
// TODO(timothyb89): Due to complications with globally enabling +
|
||||
// properly resetting Windows terminal ANSI processing, for now we just
|
||||
// disable color output. Otherwise, raw ANSI escapes will be visible to
|
||||
// users.
|
||||
var buf bytes.Buffer
|
||||
switch runtime.GOOS {
|
||||
case constants.WindowsOS:
|
||||
fmt.Fprint(&buf, alert.Spec.Message)
|
||||
default:
|
||||
switch alert.Spec.Severity {
|
||||
case types.AlertSeverity_HIGH:
|
||||
fmt.Fprint(&buf, Color(Red, alert.Spec.Message))
|
||||
case types.AlertSeverity_MEDIUM:
|
||||
fmt.Fprint(&buf, Color(Yellow, alert.Spec.Message))
|
||||
default:
|
||||
fmt.Fprint(&buf, alert.Spec.Message)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -27,6 +28,7 @@ import (
|
|||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/api/types/events"
|
||||
"github.com/gravitational/teleport/lib/asciitable"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
@ -84,7 +86,7 @@ func (e *SessionsCollection) WriteYAML(w io.Writer) error {
|
|||
return utils.WriteYAML(w, e.SessionEvents)
|
||||
}
|
||||
|
||||
// ShowSessions is s helper function for displaying listed sessions via tsh or tctl
|
||||
// ShowSessions is a helper function for displaying listed sessions via tsh or tctl.
|
||||
func ShowSessions(events []events.AuditEvent, format string, w io.Writer) error {
|
||||
sessions := &SessionsCollection{SessionEvents: events}
|
||||
switch format {
|
||||
|
@ -98,3 +100,35 @@ func ShowSessions(events []events.AuditEvent, format string, w io.Writer) error
|
|||
return trace.BadParameter("unknown format %q", format)
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterAlertGetter manages getting cluster alerts.
|
||||
type ClusterAlertGetter interface {
|
||||
GetClusterAlerts(ctx context.Context, query types.GetClusterAlertsRequest) ([]types.ClusterAlert, error)
|
||||
}
|
||||
|
||||
// ShowClusterAlerts shows cluster alerts with the given labels and severity.
|
||||
func ShowClusterAlerts(ctx context.Context, client ClusterAlertGetter, w io.Writer, labels map[string]string, minSeverity, maxSeverity types.AlertSeverity) error {
|
||||
// get any "on login" alerts
|
||||
alertCtx, cancelAlertCtx := context.WithTimeout(ctx, constants.TimeoutGetClusterAlerts)
|
||||
defer cancelAlertCtx()
|
||||
alerts, err := client.GetClusterAlerts(alertCtx, types.GetClusterAlertsRequest{
|
||||
Labels: labels,
|
||||
Severity: minSeverity,
|
||||
})
|
||||
if err != nil && !trace.IsNotImplemented(err) {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
types.SortClusterAlerts(alerts)
|
||||
var errs []error
|
||||
for _, alert := range alerts {
|
||||
if err := alert.CheckMessage(); err != nil {
|
||||
errs = append(errs, trace.Errorf("invalid alert %q: %w", alert.Metadata.Name, err))
|
||||
continue
|
||||
}
|
||||
if alert.Spec.Severity <= maxSeverity {
|
||||
fmt.Fprintf(w, "%s\n\n", utils.FormatAlert(alert))
|
||||
}
|
||||
}
|
||||
return trace.NewAggregate(errs...)
|
||||
}
|
||||
|
|
91
tool/common/common_test.go
Normal file
91
tool/common/common_test.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2022 Gravitational, Inc
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type mockedAlertGetter struct {
|
||||
alerts []types.ClusterAlert
|
||||
}
|
||||
|
||||
func (mag *mockedAlertGetter) GetClusterAlerts(ctx context.Context, query types.GetClusterAlertsRequest) ([]types.ClusterAlert, error) {
|
||||
return mag.alerts, nil
|
||||
}
|
||||
|
||||
func mockAlertGetter(alerts []types.ClusterAlert) ClusterAlertGetter {
|
||||
return &mockedAlertGetter{
|
||||
alerts: alerts,
|
||||
}
|
||||
}
|
||||
|
||||
func TestShowClusterAlerts(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
alerts []types.ClusterAlert
|
||||
wantOut string
|
||||
}{
|
||||
"No filtered severities": {
|
||||
alerts: []types.ClusterAlert{
|
||||
{
|
||||
Spec: types.ClusterAlertSpec{
|
||||
Severity: types.AlertSeverity_MEDIUM,
|
||||
Message: "someMessage",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOut: "\x1b[33msomeMessage\x1b[0m\n\n",
|
||||
},
|
||||
"Filtered severities": {
|
||||
alerts: []types.ClusterAlert{
|
||||
{
|
||||
ResourceHeader: types.ResourceHeader{
|
||||
Metadata: types.Metadata{
|
||||
Labels: map[string]string{
|
||||
"someLabel": "yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: types.ClusterAlertSpec{
|
||||
Severity: types.AlertSeverity_HIGH,
|
||||
Message: "someOtherMessage",
|
||||
},
|
||||
}, {
|
||||
Spec: types.ClusterAlertSpec{
|
||||
Severity: types.AlertSeverity_MEDIUM,
|
||||
Message: "someMessage",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
wantOut: "\x1b[33msomeMessage\x1b[0m\n\n",
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
alertGetter := mockAlertGetter(test.alerts)
|
||||
var got bytes.Buffer
|
||||
err := ShowClusterAlerts(context.Background(), alertGetter, &got, nil, types.AlertSeverity_LOW, types.AlertSeverity_MEDIUM)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.wantOut, got.String())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ import (
|
|||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/teleport/tool/common"
|
||||
toolcommon "github.com/gravitational/teleport/tool/common"
|
||||
)
|
||||
|
||||
|
@ -206,6 +207,12 @@ func TryRun(commands []CLICommand, args []string) error {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := common.ShowClusterAlerts(ctx, client, os.Stderr, nil,
|
||||
types.AlertSeverity_HIGH, types.AlertSeverity_HIGH); err != nil {
|
||||
log.WithError(err).Warn("Failed to display cluster alerts.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1584,24 +1584,14 @@ func onLogin(cf *CLIConf) error {
|
|||
fmt.Fprintf(os.Stderr, "%s\n\n", warning)
|
||||
}
|
||||
|
||||
// get any "on login" alerts
|
||||
alerts, err := tc.GetClusterAlerts(cf.Context, types.GetClusterAlertsRequest{
|
||||
Labels: map[string]string{
|
||||
// Show on-login alerts, all high severity alerts are shown by onStatus
|
||||
// so can be excluded here.
|
||||
if err := common.ShowClusterAlerts(cf.Context, tc, os.Stderr, map[string]string{
|
||||
types.AlertOnLogin: "yes",
|
||||
},
|
||||
})
|
||||
if err != nil && !trace.IsNotImplemented(err) {
|
||||
return trace.Wrap(err)
|
||||
}, types.AlertSeverity_LOW, types.AlertSeverity_MEDIUM); err != nil {
|
||||
log.WithError(err).Warn("Failed to display cluster alerts.")
|
||||
}
|
||||
|
||||
types.SortClusterAlerts(alerts)
|
||||
|
||||
for _, alert := range alerts {
|
||||
if err := alert.CheckMessage(); err != nil {
|
||||
log.Warnf("Skipping invalid alert %q: %v", alert.Metadata.Name, err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s\n\n", alert.Spec.Message)
|
||||
}
|
||||
// NOTE: we currently print all alerts that are marked as `on-login`, because we
|
||||
// don't use the alert API very heavily. If we start to make more use of it, we
|
||||
// could probably add a separate `tsh alerts ls` command, and truncate the list
|
||||
|
@ -3425,6 +3415,17 @@ func onStatus(cf *CLIConf) error {
|
|||
return trace.NotFound("Active profile expired.")
|
||||
}
|
||||
|
||||
tc, err := makeClient(cf, true)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Failed to make client for retrieving cluster alerts.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := common.ShowClusterAlerts(cf.Context, tc, os.Stderr, nil,
|
||||
types.AlertSeverity_HIGH, types.AlertSeverity_HIGH); err != nil {
|
||||
log.WithError(err).Warn("Failed to display cluster alerts.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue