Skip to content

Commit

Permalink
POR-1781 associated attached env versions with revision (#3609)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards authored Sep 20, 2023
1 parent 0091d6b commit 40daca3
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 91 deletions.
105 changes: 60 additions & 45 deletions api/server/handlers/porter_app/update_app_environment_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ type UpdateAppEnvironmentRequest struct {

// UpdateAppEnvironmentResponse represents the fields on the response object from the /apps/{porter_app_name}/environment-group endpoint
type UpdateAppEnvironmentResponse struct {
EnvGroupName string `json:"env_group_name"`
EnvGroupVersion int `json:"env_group_version"`
EnvGroups []environment_groups.EnvironmentGroup `json:"env_groups"`
}

// ServeHTTP updates or creates the environment group for an app
Expand Down Expand Up @@ -221,21 +220,24 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
envGroups: appProto.EnvGroups,
appName: appName,
appEnvName: appEnvGroupName,
sameAppEnv: true,
namespace: namespace,
deploymentTargetID: request.DeploymentTargetID,
k8sAgent: agent,
}
err = syncLatestEnvGroupVersions(ctx, syncInp)
latestEnvGroups, err := syncLatestEnvGroupVersions(ctx, syncInp)
if err != nil {
err := telemetry.Error(ctx, span, err, "error syncing latest env group versions")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

latestEnvGroups = append(latestEnvGroups, environment_groups.EnvironmentGroup{
Name: latestEnvironmentGroup.Name,
Version: latestEnvironmentGroup.Version,
})

res := &UpdateAppEnvironmentResponse{
EnvGroupName: latestEnvironmentGroup.Name,
EnvGroupVersion: latestEnvironmentGroup.Version,
EnvGroups: latestEnvGroups,
}

c.WriteResult(w, r, res)
Expand Down Expand Up @@ -288,47 +290,50 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
TargetNamespace: namespace,
}

syncedEnvironment, err := environment_groups.SyncLatestVersionToNamespace(ctx, agent, inp, additionalEnvGroupLabels)
syncedAppEnvironment, err := environment_groups.SyncLatestVersionToNamespace(ctx, agent, inp, additionalEnvGroupLabels)
if err != nil {
err := telemetry.Error(ctx, span, err, "unable to create or update synced environment group")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-versioned-name", Value: syncedEnvironment.EnvironmentGroupVersionedName})

split := strings.Split(syncedEnvironment.EnvironmentGroupVersionedName, ".")
if len(split) != 2 {
err := telemetry.Error(ctx, span, err, "unexpected environment group versioned name")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

version, err := strconv.Atoi(split[1])
if err != nil {
err := telemetry.Error(ctx, span, err, "error converting environment group version to int")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-versioned-name", Value: syncedAppEnvironment.EnvironmentGroupVersionedName})

syncInp := syncLatestEnvGroupVersionsInput{
envGroups: appProto.EnvGroups,
appName: appName,
appEnvName: appEnvGroupName,
sameAppEnv: false,
namespace: namespace,
deploymentTargetID: request.DeploymentTargetID,
k8sAgent: agent,
}
err = syncLatestEnvGroupVersions(ctx, syncInp)
latestEnvGroups, err := syncLatestEnvGroupVersions(ctx, syncInp)
if err != nil {
err := telemetry.Error(ctx, span, err, "error syncing latest env group versions")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

split := strings.Split(syncedAppEnvironment.EnvironmentGroupVersionedName, ".")
if len(split) != 2 {
err := telemetry.Error(ctx, span, err, "unexpected environment group versioned name")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

version, err := strconv.Atoi(split[1])
if err != nil {
err := telemetry.Error(ctx, span, err, "error converting environment group version to int")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

latestEnvGroups = append(latestEnvGroups, environment_groups.EnvironmentGroup{
Name: split[0],
Version: version,
})

res := &UpdateAppEnvironmentResponse{
EnvGroupName: split[0],
EnvGroupVersion: version,
EnvGroups: latestEnvGroups,
}

c.WriteResult(w, r, res)
Expand All @@ -341,8 +346,6 @@ type syncLatestEnvGroupVersionsInput struct {
appName string
// appEnvName is the name of the app env. This is the env group created when the app is created for storing app-specific variables
appEnvName string
// sameAppEnv is true if the app env group variables are unchanged. If true, we do not need to sync the latest version of the app env group
sameAppEnv bool
// namespace is the namespace to sync the latest versions to
namespace string
// deploymentTargetID is the id of the deployment target
Expand All @@ -352,54 +355,66 @@ type syncLatestEnvGroupVersionsInput struct {
}

// syncLatestEnvGroupVersions syncs the latest versions of the env groups to the namespace where an app is deployed
func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersionsInput) error {
func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersionsInput) ([]environment_groups.EnvironmentGroup, error) {
ctx, span := telemetry.NewSpan(ctx, "sync-latest-env-group-versions")
defer span.End()

var envGroups []environment_groups.EnvironmentGroup

if inp.deploymentTargetID == "" {
return telemetry.Error(ctx, span, nil, "deployment target id is empty")
return envGroups, telemetry.Error(ctx, span, nil, "deployment target id is empty")
}
if inp.appName == "" {
return telemetry.Error(ctx, span, nil, "app name is empty")
return envGroups, telemetry.Error(ctx, span, nil, "app name is empty")
}
if inp.appEnvName == "" {
return telemetry.Error(ctx, span, nil, "app env name is empty")
return envGroups, telemetry.Error(ctx, span, nil, "app env name is empty")
}
if inp.namespace == "" {
return telemetry.Error(ctx, span, nil, "namespace is empty")
return envGroups, telemetry.Error(ctx, span, nil, "namespace is empty")
}
if inp.k8sAgent == nil {
return telemetry.Error(ctx, span, nil, "k8s agent is nil")
return envGroups, telemetry.Error(ctx, span, nil, "k8s agent is nil")
}

for _, envGroup := range inp.envGroups {
if envGroup == nil {
continue
}
if envGroup.GetName() == inp.appEnvName {
continue
}

additionalEnvGroupLabels := map[string]string{
LabelKey_AppName: inp.appName,
LabelKey_DeploymentTargetID: inp.deploymentTargetID,
LabelKey_PorterManaged: "true",
}

if envGroup.GetName() == inp.appEnvName {
if inp.sameAppEnv {
continue
}

additionalEnvGroupLabels[environment_groups.LabelKey_DefaultAppEnvironment] = "true"
}

_, err := environment_groups.SyncLatestVersionToNamespace(ctx, inp.k8sAgent, environment_groups.SyncLatestVersionToNamespaceInput{
syncedEnvironment, err := environment_groups.SyncLatestVersionToNamespace(ctx, inp.k8sAgent, environment_groups.SyncLatestVersionToNamespaceInput{
TargetNamespace: inp.namespace,
BaseEnvironmentGroupName: envGroup.GetName(),
}, additionalEnvGroupLabels)
if err != nil {
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-name", Value: envGroup.GetName()})
return telemetry.Error(ctx, span, err, "error syncing latest version to namespace")
return envGroups, telemetry.Error(ctx, span, err, "error syncing latest version to namespace")
}

split := strings.Split(syncedEnvironment.EnvironmentGroupVersionedName, ".")
if len(split) != 2 {
return envGroups, telemetry.Error(ctx, span, err, "unexpected environment group versioned name")
}

version, err := strconv.Atoi(split[1])
if err != nil {
return envGroups, telemetry.Error(ctx, span, err, "error converting environment group version to int")
}

envGroups = append(envGroups, environment_groups.EnvironmentGroup{
Name: split[0],
Version: version,
})
}

return nil
return envGroups, nil
}
23 changes: 9 additions & 14 deletions cli/cmd/v2/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/porter-dev/porter/api/server/handlers/porter_app"
"github.com/porter-dev/porter/api/types"
"github.com/porter-dev/porter/internal/kubernetes/environment_groups"
"github.com/porter-dev/porter/internal/models"

"github.com/cli/cli/git"
Expand Down Expand Up @@ -78,7 +79,7 @@ func Apply(ctx context.Context, cliConf config.CLIConfig, client api.Client, por
return fmt.Errorf("error calling create or update app environment group endpoint: %w", err)
}

b64AppProto, err = updateAppEnvGroupInProto(ctx, b64AppProto, envGroupResp.EnvGroupName, envGroupResp.EnvGroupVersion)
b64AppProto, err = updateEnvGroupsInProto(ctx, b64AppProto, envGroupResp.EnvGroups)
if err != nil {
return fmt.Errorf("error updating app env group in proto: %w", err)
}
Expand Down Expand Up @@ -355,7 +356,7 @@ func imageTagFromBase64AppProto(base64AppProto string) (string, error) {
return app.Image.Tag, nil
}

func updateAppEnvGroupInProto(ctx context.Context, base64AppProto string, envGroupName string, envGroupVersion int) (string, error) {
func updateEnvGroupsInProto(ctx context.Context, base64AppProto string, envGroups []environment_groups.EnvironmentGroup) (string, error) {
var editedB64AppProto string

decoded, err := base64.StdEncoding.DecodeString(base64AppProto)
Expand All @@ -369,20 +370,14 @@ func updateAppEnvGroupInProto(ctx context.Context, base64AppProto string, envGro
return editedB64AppProto, fmt.Errorf("unable to unmarshal app for revision: %w", err)
}

envGroupExists := false
for _, envGroup := range app.EnvGroups {
if envGroup.Name == envGroupName {
envGroup.Version = int64(envGroupVersion)
envGroupExists = true
break
}
}
if !envGroupExists {
app.EnvGroups = append(app.EnvGroups, &porterv1.EnvGroup{
Name: envGroupName,
Version: int64(envGroupVersion),
egs := make([]*porterv1.EnvGroup, 0)
for _, envGroup := range envGroups {
egs = append(egs, &porterv1.EnvGroup{
Name: envGroup.Name,
Version: int64(envGroup.Version),
})
}
app.EnvGroups = egs

marshalled, err := helpers.MarshalContractObject(ctx, app)
if err != nil {
Expand Down
24 changes: 11 additions & 13 deletions dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,25 +181,23 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
}
);

const updatedEnvGroup = z
const updatedEnvGroups = z
.object({
env_group_name: z.string(),
env_group_version: z.coerce.bigint(),
env_groups: z
.object({
name: z.string(),
latest_version: z.coerce.bigint(),
})
.array(),
})
.parse(res.data);

const protoWithUpdatedEnv = new PorterApp({
...validatedAppProto,
envGroups: validatedAppProto.envGroups.map((envGroup) => {
if (envGroup.name === updatedEnvGroup.env_group_name) {
return {
...envGroup,
version: updatedEnvGroup.env_group_version,
};
}

return envGroup;
}),
envGroups: updatedEnvGroups.env_groups.map((eg) => ({
name: eg.name,
version: eg.latest_version,
})),
});

await api.applyApp(
Expand Down
37 changes: 19 additions & 18 deletions dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
porterYamlFound,
detectedName,
loading: isLoadingPorterYaml,
} = usePorterYaml({ source: source?.type === "github" ? source : null, appName: name.value });
} = usePorterYaml({
source: source?.type === "github" ? source : null,
appName: name.value,
});
const deploymentTarget = useDefaultDeploymentTarget();
const { updateAppStep } = useAppAnalytics(name.value);
const { validateApp } = useAppValidation({
Expand Down Expand Up @@ -257,7 +260,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
}
);

const envGroupResponse = await api.updateEnvironmentGroupV2(
const res = await api.updateEnvironmentGroupV2(
"<token>",
{
deployment_target_id: deploymentTarget.deployment_target_id,
Expand All @@ -272,31 +275,29 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
}
);

const addedEnvGroup = await z
const updatedEnvGroups = z
.object({
env_group_name: z.string(),
env_group_version: z.coerce.bigint(),
env_groups: z
.object({
name: z.string(),
latest_version: z.coerce.bigint(),
})
.array(),
})
.parseAsync(envGroupResponse.data);
.parse(res.data);

const envGroups = [
...app.envGroups.filter(
(group) => group.name !== addedEnvGroup.env_group_name
),
{
name: addedEnvGroup.env_group_name,
version: addedEnvGroup.env_group_version,
},
];
const appWithSeededEnv = new PorterApp({
const protoWithUpdatedEnv = new PorterApp({
...app,
envGroups,
envGroups: updatedEnvGroups.env_groups.map((eg) => ({
name: eg.name,
version: eg.latest_version,
})),
});

await api.applyApp(
"<token>",
{
b64_app_proto: btoa(appWithSeededEnv.toJsonString()),
b64_app_proto: btoa(protoWithUpdatedEnv.toJsonString()),
deployment_target_id: deploymentTarget.deployment_target_id,
},
{
Expand Down
2 changes: 1 addition & 1 deletion internal/kubernetes/environment_groups/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type EnvironmentGroup struct {
// SecretVariables are secret values for the EnvironmentGroup. This usually will be a Secret on the kubernetes cluster
SecretVariables map[string]string `json:"secret_variables,omitempty"`
// CreatedAt is only used for display purposes and is in UTC Unix time
CreatedAtUTC time.Time `json:"created_at"`
CreatedAtUTC time.Time `json:"created_at,omitempty"`
}

type environmentGroupOptions struct {
Expand Down

0 comments on commit 40daca3

Please sign in to comment.