Skip to content

Commit 220f3c0

Browse files
d-g-townjusrhee
andauthored
support doppler (#4061)
Co-authored-by: jusrhee <[email protected]>
1 parent d513670 commit 220f3c0

File tree

30 files changed

+1082
-277
lines changed

30 files changed

+1082
-277
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package environment_groups
2+
3+
import (
4+
"net/http"
5+
6+
"connectrpc.com/connect"
7+
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
8+
9+
"github.com/porter-dev/porter/api/server/authz"
10+
"github.com/porter-dev/porter/api/server/handlers"
11+
"github.com/porter-dev/porter/api/server/shared"
12+
"github.com/porter-dev/porter/api/server/shared/apierrors"
13+
"github.com/porter-dev/porter/api/server/shared/config"
14+
"github.com/porter-dev/porter/api/types"
15+
"github.com/porter-dev/porter/internal/models"
16+
"github.com/porter-dev/porter/internal/telemetry"
17+
)
18+
19+
// AreExternalProvidersEnabledHandler is the handler for the /environment-group/are-external-providers-enabled endpoint
20+
type AreExternalProvidersEnabledHandler struct {
21+
handlers.PorterHandlerReadWriter
22+
authz.KubernetesAgentGetter
23+
}
24+
25+
// NewAreExternalProvidersEnabledHandler creates an instance of AreExternalProvidersEnabledHandler
26+
func NewAreExternalProvidersEnabledHandler(
27+
config *config.Config,
28+
decoderValidator shared.RequestDecoderValidator,
29+
writer shared.ResultWriter,
30+
) *AreExternalProvidersEnabledHandler {
31+
return &AreExternalProvidersEnabledHandler{
32+
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
33+
KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
34+
}
35+
}
36+
37+
// AreExternalProvidersEnabledResponse is the response object for the /environment-group/are-external-providers-enabled endpoint
38+
type AreExternalProvidersEnabledResponse struct {
39+
// Enabled is true if external providers are enabled
40+
Enabled bool `json:"enabled"`
41+
// ReprovisionRequired is true if the cluster needs to be reprovisioned to enable external providers
42+
ReprovisionRequired bool `json:"reprovision_required"`
43+
// K8SUpgradeRequired is true if the cluster needs to be upgraded to v1.27 to enable external providers
44+
K8SUpgradeRequired bool `json:"k8s_upgrade_required"`
45+
}
46+
47+
// ServeHTTP checks if external providers are enabled
48+
func (c *AreExternalProvidersEnabledHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
49+
ctx, span := telemetry.NewSpan(r.Context(), "serve-are-external-providers-enabled")
50+
defer span.End()
51+
52+
project, _ := ctx.Value(types.ProjectScope).(*models.Project)
53+
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
54+
55+
resp, err := c.Config().ClusterControlPlaneClient.AreExternalEnvGroupProvidersEnabled(ctx, connect.NewRequest(&porterv1.AreExternalEnvGroupProvidersEnabledRequest{
56+
ProjectId: int64(project.ID),
57+
ClusterId: int64(cluster.ID),
58+
}))
59+
if err != nil {
60+
err := telemetry.Error(ctx, span, err, "unable to check if external providers are enabled")
61+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
62+
return
63+
}
64+
65+
c.WriteResult(w, r, &AreExternalProvidersEnabledResponse{
66+
Enabled: resp.Msg.Enabled,
67+
ReprovisionRequired: resp.Msg.ReprovisionRequired,
68+
K8SUpgradeRequired: resp.Msg.K8SUpgradeRequired,
69+
})
70+
}

api/server/handlers/environment_groups/create.go

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"net/http"
55
"time"
66

7+
"connectrpc.com/connect"
8+
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
9+
710
"github.com/porter-dev/porter/api/server/authz"
811
"github.com/porter-dev/porter/api/server/handlers"
912
"github.com/porter-dev/porter/api/server/shared"
@@ -35,6 +38,12 @@ type UpdateEnvironmentGroupRequest struct {
3538
// Name of the env group to create or update
3639
Name string `json:"name"`
3740

41+
// Type of the env group to create or update
42+
Type string `json:"type"`
43+
44+
// AuthToken for the env group
45+
AuthToken string `json:"auth_token"`
46+
3847
// Variables are values which are not sensitive. All values must be a string due to a kubernetes limitation.
3948
Variables map[string]string `json:"variables"`
4049

@@ -66,6 +75,7 @@ func (c *UpdateEnvironmentGroupHandler) ServeHTTP(w http.ResponseWriter, r *http
6675

6776
telemetry.WithAttributes(span,
6877
telemetry.AttributeKV{Key: "environment-group-name", Value: request.Name},
78+
telemetry.AttributeKV{Key: "environment-group-type", Value: request.Type},
6979
)
7080

7181
agent, err := c.GetAgent(r, cluster, "")
@@ -75,34 +85,45 @@ func (c *UpdateEnvironmentGroupHandler) ServeHTTP(w http.ResponseWriter, r *http
7585
return
7686
}
7787

78-
envGroup := environment_groups.EnvironmentGroup{
79-
Name: request.Name,
80-
Variables: request.Variables,
81-
SecretVariables: request.SecretVariables,
82-
CreatedAtUTC: time.Now().UTC(),
83-
}
84-
85-
err = environment_groups.CreateOrUpdateBaseEnvironmentGroup(ctx, agent, envGroup, nil)
86-
if err != nil {
87-
err := telemetry.Error(ctx, span, err, "unable to create or update environment group")
88-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
89-
return
88+
var envGroup environment_groups.EnvironmentGroup
89+
switch request.Type {
90+
case "doppler":
91+
_, err := c.Config().ClusterControlPlaneClient.CreateOrUpdateEnvGroup(ctx, connect.NewRequest(&porterv1.CreateOrUpdateEnvGroupRequest{
92+
ProjectId: int64(cluster.ProjectID),
93+
ClusterId: int64(cluster.ID),
94+
EnvGroupProviderType: porterv1.EnumEnvGroupProviderType_ENUM_ENV_GROUP_PROVIDER_TYPE_DOPPLER,
95+
EnvGroupName: request.Name,
96+
EnvGroupAuthToken: request.AuthToken,
97+
}))
98+
if err != nil {
99+
err := telemetry.Error(ctx, span, err, "unable to create environment group")
100+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
101+
return
102+
}
103+
104+
envGroup = environment_groups.EnvironmentGroup{
105+
Name: request.Name,
106+
CreatedAtUTC: time.Now().UTC(),
107+
}
108+
default:
109+
envGroup := environment_groups.EnvironmentGroup{
110+
Name: request.Name,
111+
Variables: request.Variables,
112+
SecretVariables: request.SecretVariables,
113+
CreatedAtUTC: time.Now().UTC(),
114+
}
115+
116+
err = environment_groups.CreateOrUpdateBaseEnvironmentGroup(ctx, agent, envGroup, nil)
117+
if err != nil {
118+
err := telemetry.Error(ctx, span, err, "unable to create or update environment group")
119+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
120+
return
121+
}
90122
}
91123

92124
envGroupResponse := &UpdateEnvironmentGroupResponse{
93125
Name: envGroup.Name,
94126
CreatedAt: envGroup.CreatedAtUTC,
95127
}
96128
c.WriteResult(w, r, envGroupResponse)
97-
98-
// TODO: Syncing applications that are linked is currently done by the frontend. This should be done entirely
99-
// applicationsToSync, err := environment_groups.LinkedApplications(ctx, agent, envGroup.Name)
100-
// if err != nil {
101-
// err := telemetry.Error(ctx, span, err, "unable to find linked applications for environment group")
102-
// c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
103-
// return
104-
// }
105-
// for _, app := range applicationsToSync {
106-
// TODO: Call porter app update
107-
// }
108129
}

api/server/handlers/environment_groups/delete.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,27 @@ package environment_groups
33
import (
44
"net/http"
55

6+
"connectrpc.com/connect"
7+
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
8+
"github.com/porter-dev/porter/internal/kubernetes/environment_groups"
9+
610
"github.com/porter-dev/porter/api/server/authz"
711
"github.com/porter-dev/porter/api/server/handlers"
812
"github.com/porter-dev/porter/api/server/shared"
913
"github.com/porter-dev/porter/api/server/shared/apierrors"
1014
"github.com/porter-dev/porter/api/server/shared/config"
1115
"github.com/porter-dev/porter/api/types"
12-
"github.com/porter-dev/porter/internal/kubernetes/environment_groups"
1316
"github.com/porter-dev/porter/internal/models"
1417
"github.com/porter-dev/porter/internal/telemetry"
1518
)
1619

20+
// DeleteEnvironmentGroupHandler is the handler for the DELETE /environment-group endpoint
1721
type DeleteEnvironmentGroupHandler struct {
1822
handlers.PorterHandlerReadWriter
1923
authz.KubernetesAgentGetter
2024
}
2125

26+
// NewDeleteEnvironmentGroupHandler creates an instance of DeleteEnvironmentGroupHandler
2227
func NewDeleteEnvironmentGroupHandler(
2328
config *config.Config,
2429
decoderValidator shared.RequestDecoderValidator,
@@ -30,11 +35,16 @@ func NewDeleteEnvironmentGroupHandler(
3035
}
3136
}
3237

38+
// DeleteEnvironmentGroupRequest is the request object for the DELETE /environment-group endpoint
3339
type DeleteEnvironmentGroupRequest struct {
3440
// Name of the env group to delete
3541
Name string `json:"name"`
42+
43+
// Type of the env group to delete
44+
Type string `json:"type"`
3645
}
3746

47+
// ServeHTTP deletes an environment group
3848
func (c *DeleteEnvironmentGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3949
ctx, span := telemetry.NewSpan(r.Context(), "serve-delete-env-group")
4050
defer span.End()
@@ -45,27 +55,37 @@ func (c *DeleteEnvironmentGroupHandler) ServeHTTP(w http.ResponseWriter, r *http
4555
}
4656
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
4757

48-
if request.Name == "" {
49-
err := telemetry.Error(ctx, span, nil, "environment group name is required for deletion")
50-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
51-
return
52-
}
53-
5458
telemetry.WithAttributes(span,
5559
telemetry.AttributeKV{Key: "environment-group-name", Value: request.Name},
60+
telemetry.AttributeKV{Key: "environment-group-type", Value: request.Type},
5661
)
5762

58-
agent, err := c.GetAgent(r, cluster, "")
59-
if err != nil {
60-
err := telemetry.Error(ctx, span, err, "unable to connect to kubernetes cluster")
61-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
62-
return
63-
}
63+
switch request.Type {
64+
case "doppler":
65+
_, err := c.Config().ClusterControlPlaneClient.DeleteEnvGroup(ctx, connect.NewRequest(&porterv1.DeleteEnvGroupRequest{
66+
ProjectId: int64(cluster.ProjectID),
67+
ClusterId: int64(cluster.ID),
68+
EnvGroupProviderType: porterv1.EnumEnvGroupProviderType_ENUM_ENV_GROUP_PROVIDER_TYPE_DOPPLER,
69+
EnvGroupName: request.Name,
70+
}))
71+
if err != nil {
72+
err := telemetry.Error(ctx, span, err, "unable to create environment group")
73+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
74+
return
75+
}
76+
default:
77+
agent, err := c.GetAgent(r, cluster, "")
78+
if err != nil {
79+
err := telemetry.Error(ctx, span, err, "unable to connect to kubernetes cluster")
80+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
81+
return
82+
}
6483

65-
err = environment_groups.DeleteEnvironmentGroup(ctx, agent, request.Name)
66-
if err != nil {
67-
err := telemetry.Error(ctx, span, err, "unable to delete environment group")
68-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
69-
return
84+
err = environment_groups.DeleteEnvironmentGroup(ctx, agent, request.Name)
85+
if err != nil {
86+
err := telemetry.Error(ctx, span, err, "unable to delete environment group")
87+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
88+
return
89+
}
7090
}
7191
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package environment_groups
2+
3+
import (
4+
"net/http"
5+
6+
"connectrpc.com/connect"
7+
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
8+
9+
"github.com/porter-dev/porter/api/server/authz"
10+
"github.com/porter-dev/porter/api/server/handlers"
11+
"github.com/porter-dev/porter/api/server/shared"
12+
"github.com/porter-dev/porter/api/server/shared/apierrors"
13+
"github.com/porter-dev/porter/api/server/shared/config"
14+
"github.com/porter-dev/porter/api/types"
15+
"github.com/porter-dev/porter/internal/models"
16+
"github.com/porter-dev/porter/internal/telemetry"
17+
)
18+
19+
// EnableExternalProvidersHandler is the handler for the /environment-groups/enable-external-providers endpoint
20+
type EnableExternalProvidersHandler struct {
21+
handlers.PorterHandlerReadWriter
22+
authz.KubernetesAgentGetter
23+
}
24+
25+
// NewEnableExternalProvidersHandler creates an instance of EnableExternalProvidersHandler
26+
func NewEnableExternalProvidersHandler(
27+
config *config.Config,
28+
decoderValidator shared.RequestDecoderValidator,
29+
writer shared.ResultWriter,
30+
) *EnableExternalProvidersHandler {
31+
return &EnableExternalProvidersHandler{
32+
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
33+
KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
34+
}
35+
}
36+
37+
// EnableExternalProvidersResponse is the response object for the /environment-groups/enable-external-providers endpoint
38+
func (c *EnableExternalProvidersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
39+
ctx, span := telemetry.NewSpan(r.Context(), "serve-enable-external-providers")
40+
defer span.End()
41+
42+
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
43+
44+
_, err := c.Config().ClusterControlPlaneClient.EnableExternalEnvGroupProviders(ctx, connect.NewRequest(&porterv1.EnableExternalEnvGroupProvidersRequest{
45+
ProjectId: int64(cluster.ProjectID),
46+
ClusterId: int64(cluster.ID),
47+
}))
48+
if err != nil {
49+
err := telemetry.Error(ctx, span, err, "unable to enable external providers")
50+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
51+
return
52+
}
53+
}

api/server/handlers/environment_groups/list.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,19 @@ func NewListEnvironmentGroupsHandler(
3232
}
3333
}
3434

35+
// ListEnvironmentGroupsRequest is the request object for the /environment-groups endpoint
36+
type ListEnvironmentGroupsRequest struct {
37+
// Type of the env group to filter by. If empty, all env groups will be returned.
38+
Type string `json:"type"`
39+
}
40+
3541
type ListEnvironmentGroupsResponse struct {
3642
EnvironmentGroups []EnvironmentGroupListItem `json:"environment_groups,omitempty"`
3743
}
3844

3945
type EnvironmentGroupListItem struct {
4046
Name string `json:"name"`
47+
Type string `json:"type"`
4148
LatestVersion int `json:"latest_version"`
4249
Variables map[string]string `json:"variables,omitempty"`
4350
SecretVariables map[string]string `json:"secret_variables,omitempty"`
@@ -52,6 +59,15 @@ func (c *ListEnvironmentGroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
5259
project, _ := ctx.Value(types.ProjectScope).(*models.Project)
5360
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
5461

62+
request := &ListEnvironmentGroupsRequest{}
63+
if ok := c.DecodeAndValidate(w, r, request); !ok {
64+
err := telemetry.Error(ctx, span, nil, "unable to decode or validate request body")
65+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
66+
return
67+
}
68+
69+
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-type", Value: request.Type})
70+
5571
agent, err := c.GetAgent(r, cluster, "")
5672
if err != nil {
5773
err = telemetry.Error(ctx, span, err, "unable to connect to cluster")
@@ -66,6 +82,16 @@ func (c *ListEnvironmentGroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
6682
return
6783
}
6884

85+
if request.Type != "" {
86+
var filteredEnvGroupVersions []environmentgroups.EnvironmentGroup
87+
for _, envGroup := range allEnvGroupVersions {
88+
if envGroup.Type == request.Type {
89+
filteredEnvGroupVersions = append(filteredEnvGroupVersions, envGroup)
90+
}
91+
}
92+
allEnvGroupVersions = filteredEnvGroupVersions
93+
}
94+
6995
envGroupSet := make(map[string]struct{})
7096
for _, envGroup := range allEnvGroupVersions {
7197
if envGroup.Name == "" {
@@ -126,6 +152,7 @@ func (c *ListEnvironmentGroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
126152
}
127153
envGroups = append(envGroups, EnvironmentGroupListItem{
128154
Name: latestVersion.Name,
155+
Type: latestVersion.Type,
129156
LatestVersion: latestVersion.Version,
130157
Variables: latestVersion.Variables,
131158
SecretVariables: secrets,

0 commit comments

Comments
 (0)