Skip to content

Commit 628f890

Browse files
authored
Add plugin resource handling in tctl (#42223)
1 parent 3ea1324 commit 628f890

File tree

9 files changed

+570
-161
lines changed

9 files changed

+570
-161
lines changed

api/gen/proto/go/teleport/plugins/v1/plugin_service.pb.go

Lines changed: 235 additions & 161 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/proto/teleport/plugins/v1/plugin_service.proto

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ message GetPluginRequest {
6868
bool with_secrets = 2;
6969
}
7070

71+
// UpdatePluginRequest is a request to update a plugin instance.
72+
message UpdatePluginRequest {
73+
// Plugin is the plugin object to update.
74+
types.PluginV1 plugin = 1;
75+
}
76+
7177
// ListPluginsRequest is a paginated request to list all plugin instances.
7278
message ListPluginsRequest {
7379
// PageSize is the maximum number of plugins to return in a single response.
@@ -180,6 +186,9 @@ service PluginService {
180186
// GetPlugin returns a plugin instance by name.
181187
rpc GetPlugin(GetPluginRequest) returns (types.PluginV1);
182188

189+
// UpdatePlugin updates a plugin instance.
190+
rpc UpdatePlugin(UpdatePluginRequest) returns (types.PluginV1);
191+
183192
// DeletePlugin removes the specified plugin instance.
184193
rpc DeletePlugin(DeletePluginRequest) returns (google.protobuf.Empty);
185194

lib/services/local/plugins.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ func (s *PluginsService) DeletePlugin(ctx context.Context, name string) error {
7272
return nil
7373
}
7474

75+
// UpdatePlugin updates a plugin resource.
76+
func (s *PluginsService) UpdatePlugin(ctx context.Context, plugin types.Plugin) (types.Plugin, error) {
77+
if err := services.CheckAndSetDefaults(plugin); err != nil {
78+
return nil, trace.Wrap(err)
79+
}
80+
value, err := services.MarshalPlugin(plugin)
81+
if err != nil {
82+
return nil, trace.Wrap(err)
83+
}
84+
item := backend.Item{
85+
Key: backend.Key(pluginsPrefix, plugin.GetName()),
86+
Value: value,
87+
Expires: plugin.Expiry(),
88+
Revision: plugin.GetRevision(),
89+
}
90+
lease, err := s.backend.ConditionalUpdate(ctx, item)
91+
if err != nil {
92+
return nil, trace.Wrap(err)
93+
}
94+
if err := types.SetRevision(plugin, lease.Revision); err != nil {
95+
return nil, trace.Wrap(err)
96+
}
97+
return plugin, nil
98+
}
99+
75100
// DeleteAllPlugins implements service.Plugins
76101
func (s *PluginsService) DeleteAllPlugins(ctx context.Context) error {
77102
startKey := backend.ExactKey(pluginsPrefix)

lib/services/plugins.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
// Plugins is the plugin service
3333
type Plugins interface {
3434
CreatePlugin(ctx context.Context, plugin types.Plugin) error
35+
UpdatePlugin(ctx context.Context, plugin types.Plugin) (types.Plugin, error)
3536
DeleteAllPlugins(ctx context.Context) error
3637
DeletePlugin(ctx context.Context, name string) error
3738
GetPlugin(ctx context.Context, name string, withSecrets bool) (types.Plugin, error)

lib/services/resource.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ func ParseShortcut(in string) (string, error) {
235235
return types.KindVnetConfig, nil
236236
case types.KindAccessRequest, types.KindAccessRequest + "s", "accessrequest", "accessrequests":
237237
return types.KindAccessRequest, nil
238+
case types.KindPlugin, types.KindPlugin + "s":
239+
return types.KindPlugin, nil
238240
}
239241
return "", trace.BadParameter("unsupported resource: %q - resources should be expressed as 'type/name', for example 'connector/github'", in)
240242
}

tool/tctl/common/collection.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package common
2020

2121
import (
22+
"encoding/json"
2223
"fmt"
2324
"io"
2425
"sort"
@@ -1524,3 +1525,128 @@ func (c *accessRequestCollection) writeText(w io.Writer, verbose bool) error {
15241525
_, err := t.AsBuffer().WriteTo(w)
15251526
return trace.Wrap(err)
15261527
}
1528+
1529+
type pluginCollection struct {
1530+
plugins []types.Plugin
1531+
}
1532+
1533+
type pluginResourceWrapper struct {
1534+
types.PluginV1
1535+
}
1536+
1537+
func (p *pluginResourceWrapper) UnmarshalJSON(data []byte) error {
1538+
1539+
const (
1540+
credOauth2AccessToken = "oauth2_access_token"
1541+
credBearerToken = "bearer_token"
1542+
credIdSecret = "id_secret"
1543+
credStaticCredentialsRef = "static_credentials_ref"
1544+
settingsSlackAccessPlugin = "slack_access_plugin"
1545+
settingsOpsgenie = "opsgenie"
1546+
settingsOpenAI = "openai"
1547+
settingsOkta = "okta"
1548+
settingsJamf = "jamf"
1549+
settingsPagerDuty = "pager_duty"
1550+
settingsMattermost = "mattermost"
1551+
settingsJira = "jira"
1552+
settingsDiscord = "discord"
1553+
settingsServiceNow = "serviceNow"
1554+
settingsGitlab = "gitlab"
1555+
settingsEntraID = "entra_id"
1556+
)
1557+
type unknownPluginType struct {
1558+
Spec struct {
1559+
Settings map[string]json.RawMessage `json:"Settings"`
1560+
} `json:"spec"`
1561+
Credentials struct {
1562+
Credentials map[string]json.RawMessage `json:"Credentials"`
1563+
} `json:"credentials"`
1564+
}
1565+
1566+
var unknownPlugin unknownPluginType
1567+
if err := json.Unmarshal(data, &unknownPlugin); err != nil {
1568+
return err
1569+
}
1570+
1571+
if unknownPlugin.Spec.Settings == nil {
1572+
return trace.BadParameter("plugin settings are missing")
1573+
}
1574+
if len(unknownPlugin.Spec.Settings) != 1 {
1575+
return trace.BadParameter("unknown plugin settings count")
1576+
}
1577+
1578+
if len(unknownPlugin.Credentials.Credentials) == 1 {
1579+
p.PluginV1.Credentials = &types.PluginCredentialsV1{}
1580+
for k := range unknownPlugin.Credentials.Credentials {
1581+
switch k {
1582+
case credOauth2AccessToken:
1583+
p.PluginV1.Credentials.Credentials = &types.PluginCredentialsV1_Oauth2AccessToken{}
1584+
case credBearerToken:
1585+
p.PluginV1.Credentials.Credentials = &types.PluginCredentialsV1_BearerToken{}
1586+
case credIdSecret:
1587+
p.PluginV1.Credentials.Credentials = &types.PluginCredentialsV1_IdSecret{}
1588+
case credStaticCredentialsRef:
1589+
p.PluginV1.Credentials.Credentials = &types.PluginCredentialsV1_StaticCredentialsRef{}
1590+
default:
1591+
return trace.BadParameter("unsupported plugin credential type: %v", k)
1592+
}
1593+
}
1594+
}
1595+
1596+
for k := range unknownPlugin.Spec.Settings {
1597+
1598+
switch k {
1599+
case settingsSlackAccessPlugin:
1600+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_SlackAccessPlugin{}
1601+
case settingsOpsgenie:
1602+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Opsgenie{}
1603+
case settingsOpenAI:
1604+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Openai{}
1605+
case settingsOkta:
1606+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Okta{}
1607+
case settingsJamf:
1608+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Jamf{}
1609+
case settingsPagerDuty:
1610+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_PagerDuty{}
1611+
case settingsMattermost:
1612+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Mattermost{}
1613+
case settingsJira:
1614+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Jira{}
1615+
case settingsDiscord:
1616+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Discord{}
1617+
case settingsServiceNow:
1618+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_ServiceNow{}
1619+
case settingsGitlab:
1620+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Gitlab{}
1621+
case settingsEntraID:
1622+
p.PluginV1.Spec.Settings = &types.PluginSpecV1_EntraId{}
1623+
default:
1624+
return trace.BadParameter("unsupported plugin type: %v", k)
1625+
}
1626+
}
1627+
1628+
if err := json.Unmarshal(data, &p.PluginV1); err != nil {
1629+
return err
1630+
}
1631+
return nil
1632+
}
1633+
1634+
func (c *pluginCollection) resources() []types.Resource {
1635+
r := make([]types.Resource, len(c.plugins))
1636+
for i, resource := range c.plugins {
1637+
r[i] = resource
1638+
}
1639+
return r
1640+
}
1641+
1642+
func (c *pluginCollection) writeText(w io.Writer, verbose bool) error {
1643+
t := asciitable.MakeTable([]string{"Name", "Status"})
1644+
for _, plugin := range c.plugins {
1645+
t.AddRow([]string{
1646+
plugin.GetName(),
1647+
plugin.GetStatus().GetCode().String(),
1648+
})
1649+
}
1650+
_, err := t.AsBuffer().WriteTo(w)
1651+
return trace.Wrap(err)
1652+
}

0 commit comments

Comments
 (0)