Skip to content

Commit 169beac

Browse files
committed
Implement terraform/stringify webapi endpoint
1 parent 50cd404 commit 169beac

File tree

8 files changed

+269
-0
lines changed

8 files changed

+269
-0
lines changed

lib/web/apiserver.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,8 @@ func (h *Handler) bindDefaultEndpoints() {
10321032
h.POST("/webapi/yaml/parse/:kind", h.WithAuth(h.yamlParse))
10331033
h.POST("/webapi/yaml/stringify/:kind", h.WithAuth(h.yamlStringify))
10341034

1035+
h.POST("/webapi/terraform/stringify/:kind", h.WithAuth(h.terraformStringify))
1036+
10351037
// Desktop access endpoints.
10361038
h.GET("/webapi/sites/:site/desktops", h.WithClusterAuth(h.clusterDesktopsGet))
10371039
h.GET("/webapi/sites/:site/desktopservices", h.WithClusterAuth(h.clusterDesktopServicesGet))

lib/web/tfgen.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Teleport
3+
* Copyright (C) 2025 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
package web
20+
21+
import (
22+
"net/http"
23+
24+
"github.com/gravitational/trace"
25+
"github.com/julienschmidt/httprouter"
26+
27+
accessmonitoringrulesv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1"
28+
"github.com/gravitational/teleport/api/types"
29+
"github.com/gravitational/teleport/lib/httplib"
30+
"github.com/gravitational/teleport/lib/tfgen"
31+
)
32+
33+
type terraformStringifyResponse struct {
34+
Terraform string `json:"terraform"`
35+
}
36+
37+
func (h *Handler) terraformStringify(
38+
w http.ResponseWriter,
39+
r *http.Request,
40+
params httprouter.Params,
41+
ctx *SessionContext,
42+
) (any, error) {
43+
kind := params.ByName("kind")
44+
if len(kind) == 0 {
45+
return nil, trace.BadParameter("query param %q is required", "kind")
46+
}
47+
48+
var tfResult []byte
49+
50+
switch kind {
51+
case types.KindAccessMonitoringRule:
52+
var req struct {
53+
Resource *accessmonitoringrulesv1.AccessMonitoringRule `json:"resource"`
54+
}
55+
56+
err := httplib.ReadResourceJSON(r, &req)
57+
if err != nil {
58+
return nil, trace.Wrap(err)
59+
}
60+
61+
tfResult, err = tfgen.Generate(req.Resource)
62+
if err != nil {
63+
return nil, trace.Wrap(err)
64+
}
65+
66+
default:
67+
return nil, trace.NotImplemented("Generate Terraform for kind %q is not supported", kind)
68+
}
69+
70+
return terraformStringifyResponse{Terraform: string(tfResult)}, nil
71+
}

lib/web/tfgen_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Teleport
3+
* Copyright (C) 2025 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
package web
20+
21+
import (
22+
"context"
23+
"encoding/json"
24+
"testing"
25+
26+
"github.com/stretchr/testify/require"
27+
28+
accessmonitoringrulesv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1"
29+
"github.com/gravitational/teleport/api/types"
30+
)
31+
32+
const validAccessMonitoringRuleTerraform = `resource "teleport_access_monitoring_rule" "foo" {
33+
version = "v1"
34+
35+
metadata = {
36+
name = "foo"
37+
}
38+
39+
spec = {
40+
subjects = ["access_request"]
41+
condition = "some-condition"
42+
notification = {
43+
name = "mattermost"
44+
recipients = ["apple"]
45+
}
46+
}
47+
}
48+
`
49+
50+
func TestTerraformStringify_Valid(t *testing.T) {
51+
t.Parallel()
52+
53+
env := newWebPack(t, 1)
54+
proxy := env.proxies[0]
55+
pack := proxy.authPack(t, "test@example.com", nil)
56+
57+
req := struct {
58+
Resource *accessmonitoringrulesv1.AccessMonitoringRule `json:"resource"`
59+
}{
60+
Resource: getAccessMonitoringRuleResource(),
61+
}
62+
63+
endpoint := pack.clt.Endpoint("webapi", "terraform", "stringify", types.KindAccessMonitoringRule)
64+
re, err := pack.clt.PostJSON(context.Background(), endpoint, req)
65+
require.NoError(t, err)
66+
67+
var resp terraformStringifyResponse
68+
require.NoError(t, json.Unmarshal(re.Bytes(), &resp))
69+
require.Equal(t, validAccessMonitoringRuleTerraform, resp.Terraform)
70+
}
71+
72+
func TestTerraformStringify_Errors(t *testing.T) {
73+
t.Parallel()
74+
75+
env := newWebPack(t, 1)
76+
proxy := env.proxies[0]
77+
pack := proxy.authPack(t, "test@example.com", nil)
78+
79+
testCases := []struct {
80+
desc string
81+
kind string
82+
}{
83+
{
84+
desc: "unsupported kind",
85+
kind: "something-random",
86+
},
87+
{
88+
desc: "missing kind",
89+
kind: "",
90+
},
91+
}
92+
93+
for _, tc := range testCases {
94+
t.Run(tc.desc, func(t *testing.T) {
95+
endpoint := pack.clt.Endpoint("webapi", "terraform", "stringify", tc.kind)
96+
_, err := pack.clt.PostJSON(context.Background(), endpoint, struct {
97+
Resource *accessmonitoringrulesv1.AccessMonitoringRule `json:"resource"`
98+
}{
99+
Resource: getAccessMonitoringRuleResource(),
100+
})
101+
102+
require.Error(t, err)
103+
})
104+
}
105+
}

web/packages/teleport/src/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import generateResourcePath from './generateResourcePath';
4848
import { IntegrationTag } from './Integrations/Enroll/Shared';
4949
import type { MfaChallengeResponse } from './services/mfa';
5050
import { KindAuthConnectors } from './services/resources';
51+
import { TerraformSupportedResourceKind } from './services/tfgen/types';
5152

5253
export type Cfg = typeof cfg;
5354
const cfg = {
@@ -539,6 +540,10 @@ const cfg = {
539540
stringify: '/v1/webapi/yaml/stringify/:kind',
540541
},
541542

543+
tfgen: {
544+
stringify: '/v1/webapi/terraform/stringify/:kind',
545+
},
546+
542547
sessionRecording: {
543548
metadata:
544549
'/v1/webapi/sites/:clusterId/sessionrecording/:sessionId/metadata/ws',
@@ -1178,6 +1183,10 @@ const cfg = {
11781183
return generatePath(cfg.api.yaml.stringify, { kind });
11791184
},
11801185

1186+
getTerraformStringifyUrl(kind: TerraformSupportedResourceKind) {
1187+
return generatePath(cfg.api.tfgen.stringify, { kind });
1188+
},
1189+
11811190
getLocksRoute() {
11821191
return cfg.routes.locks;
11831192
},
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2025 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
export { tfgenService } from './tfgen';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2025 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
import cfg from 'teleport/config';
20+
import api from 'teleport/services/api';
21+
22+
import {
23+
TerraformStringifyRequest,
24+
TerraformSupportedResourceKind,
25+
} from './types';
26+
27+
export const tfgenService = {
28+
stringify<T>(
29+
kind: TerraformSupportedResourceKind,
30+
req: TerraformStringifyRequest<T>
31+
): Promise<string> {
32+
return api
33+
.post(cfg.getTerraformStringifyUrl(kind), req)
34+
.then(resp => resp.terraform);
35+
},
36+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2025 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
export type TerraformStringifyRequest<T> = {
20+
resource: T;
21+
};
22+
23+
export enum TerraformSupportedResourceKind {
24+
AccessMonitoringRule = 'access_monitoring_rule',
25+
}

web/packages/teleport/src/teleportContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import RecordingsService from './services/recordings';
3636
import ResourceService from './services/resources';
3737
import sessionService from './services/session';
3838
import { storageService } from './services/storageService';
39+
import { tfgenService } from './services/tfgen/tfgen';
3940
import userService from './services/user';
4041
import userGroupService from './services/userGroups';
4142
import { yamlService } from './services/yaml/yaml';
@@ -64,6 +65,7 @@ class TeleportContext implements types.Context {
6465
mfaService = new MfaService();
6566
notificationService = new NotificationService();
6667
yamlService = yamlService;
68+
tfgenService = tfgenService;
6769

6870
notificationContentFactory = notificationContentFactory;
6971

0 commit comments

Comments
 (0)