Skip to content

Commit

Permalink
feat: initial pass at service cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
shreddedbacon committed Jan 7, 2024
1 parent 24d525b commit cf2a8b9
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
4 changes: 4 additions & 0 deletions cmd/identify_ingress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"
"testing"

"github.com/uselagoon/build-deploy-tool/internal/helpers"
"github.com/uselagoon/build-deploy-tool/internal/lagoon"
"github.com/uselagoon/build-deploy-tool/internal/testdata"
)
Expand Down Expand Up @@ -378,6 +379,9 @@ func TestIdentifyRoute(t *testing.T) {
if string(retJSON) != tt.wantJSON {
t.Errorf("returned autogen %v doesn't match want %v", string(retJSON), tt.wantJSON)
}
t.Cleanup(func() {
helpers.UnsetEnvVars(nil)
})
})
}
}
Expand Down
70 changes: 70 additions & 0 deletions cmd/identify_services.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package cmd

import (
"encoding/json"
"fmt"

"github.com/spf13/cobra"
generator "github.com/uselagoon/build-deploy-tool/internal/generator"
"github.com/uselagoon/build-deploy-tool/internal/helpers"
)

type identifyServices struct {
Name string `json:"name"`
Type string `json:"type"`
}

var servicesIdentify = &cobra.Command{
Use: "services",
Aliases: []string{"s"},
Short: "Identify services that this build would create",
RunE: func(cmd *cobra.Command, args []string) error {
generator, err := generator.GenerateInput(*rootCmd, false)
if err != nil {
return err
}
ret, _, err := IdentifyServices(generator)
if err != nil {
return err
}
retJSON, _ := json.Marshal(ret)
fmt.Println(string(retJSON))
return nil
},
}

// IdentifyServices identifies services that this build would create
func IdentifyServices(g generator.GeneratorInput) ([]string, []identifyServices, error) {
lagoonBuild, err := generator.NewGenerator(
g,
)
if err != nil {
return nil, nil, err
}

services := []string{}
serviceTypes := []identifyServices{}
for _, service := range lagoonBuild.BuildValues.Services {
if service.Type != "" {
services = helpers.AppendIfMissing(services, service.OverrideName)
serviceTypes = AppendIfMissing(serviceTypes, identifyServices{
Name: service.OverrideName,
Type: service.Type,
})
}
}
return services, serviceTypes, nil
}

func init() {
identifyCmd.AddCommand(servicesIdentify)
}

func AppendIfMissing(slice []identifyServices, i identifyServices) []identifyServices {
for _, ele := range slice {
if ele.Name == i.Name {
return slice
}
}
return append(slice, i)
}
103 changes: 103 additions & 0 deletions cmd/identify_services_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cmd

import (
"reflect"
"testing"

"github.com/uselagoon/build-deploy-tool/internal/helpers"
"github.com/uselagoon/build-deploy-tool/internal/lagoon"
"github.com/uselagoon/build-deploy-tool/internal/testdata"
)

func TestIdentifyServices(t *testing.T) {
tests := []struct {
name string
args testdata.TestData
templatePath string
want []string
wantServices []identifyServices
wantErr bool
}{
{
name: "test1 single service",
args: testdata.GetSeedData(
testdata.TestData{
ProjectName: "example-project",
EnvironmentName: "main",
Branch: "main",
LagoonYAML: "../internal/testdata/node/lagoon.yml",
}, true),
templatePath: "testdata/output",
want: []string{"node"},
wantServices: []identifyServices{{Name: "node", Type: "node"}},
},
{
name: "test2 complex servives",
args: testdata.GetSeedData(
testdata.TestData{
ProjectName: "example-project",
EnvironmentName: "main",
Branch: "main",
EnvironmentType: "development",
LagoonYAML: "../internal/testdata/complex/lagoon.yml",
}, true),
templatePath: "testdata/output",
want: []string{"cli", "nginx-php", "mariadb", "redis"},
wantServices: []identifyServices{
{Name: "cli", Type: "cli-persistent"},
{Name: "nginx-php", Type: "nginx-php-persistent"},
{Name: "mariadb", Type: "mariadb-dbaas"},
{Name: "redis", Type: "redis"},
},
},
{
name: "test3 complex servives where one is removed",
args: testdata.GetSeedData(
testdata.TestData{
ProjectName: "example-project",
EnvironmentName: "main",
Branch: "main",
EnvironmentType: "development",
LagoonYAML: "../internal/testdata/complex/lagoon.yml",
EnvVariables: []lagoon.EnvironmentVariable{
{
Name: "LAGOON_SERVICE_TYPES",
Value: "redis:none",
Scope: "build",
},
},
}, true),
templatePath: "testdata/output",
want: []string{"cli", "nginx-php", "mariadb"},
wantServices: []identifyServices{
{Name: "cli", Type: "cli-persistent"},
{Name: "nginx-php", Type: "nginx-php-persistent"},
{Name: "mariadb", Type: "mariadb-dbaas"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// set the environment variables from args
savedTemplates := tt.templatePath
generator, err := testdata.SetupEnvironment(*rootCmd, savedTemplates, tt.args)
if err != nil {
t.Errorf("%v", err)
}
got, got2, err := IdentifyServices(generator)
if (err != nil) != tt.wantErr {
t.Errorf("IdentifyServices() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("IdentifyServices() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.wantServices) {
t.Errorf("IdentifyServices() = %v, want %v", got2, tt.wantServices)
}
t.Cleanup(func() {
helpers.UnsetEnvVars(nil)
})
})
}
}
9 changes: 9 additions & 0 deletions internal/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,12 @@ func DeepCopy(src, dist interface{}) (err error) {
}
return gob.NewDecoder(&buf).Decode(dist)
}

func AppendIfMissing(slice []string, i string) []string {
for _, ele := range slice {
if ele == i {
return slice
}
}
return append(slice, i)
}
78 changes: 78 additions & 0 deletions legacy/build-deploy-docker-compose.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,84 @@ set +x
currentStepEnd="$(date +"%Y-%m-%d %H:%M:%S")"
patchBuildStep "${buildStartTime}" "${previousStepEnd}" "${currentStepEnd}" "${NAMESPACE}" "deploymentApplyComplete" "Applying Deployments" "false"
previousStepEnd=${currentStepEnd}
beginBuildStep "Service/Deployment Cleanup" "cleanupServices"

##############################################
### CLEANUP services which have been removed from docker-compose.yaml
##############################################s

set +x
# collect the current routes, its possible to exclude ingress by adding a label 'route.lagoon.sh/remove=false' and it won't get deleted
CURRENT_SERVICES=$(kubectl -n ${NAMESPACE} get deployments -l "lagoon.sh/service-type" -l "lagoon.sh/service" --no-headers | cut -d " " -f 1 | xargs)
# collect the routes that Lagoon thinks it should have based on the .lagoon.yml and any routes that have come from the api
# using the build-deploy-tool generator
SERVICES_TO_JSON=$(build-deploy-tool identify services | jq -r '.[]')

MATCHED_SERVICE=false
DELETE_SERVICE=()
# loop over the routes from kubernetes
for EXIST_SERVICE in ${CURRENT_SERVICES}; do
# loop over the routes that Lagoon thinks it should have
for SERVICE in ${SERVICES_TO_JSON}; do
if [ "${EXIST_SERVICE}" == "${SERVICE}" ]; then
MATCHED_SERVICE=true
continue
fi
done
if [ "${MATCHED_SERVICE}" != "true" ]; then
DELETE_SERVICE+=($EXIST_SERVICE)
fi
MATCHED_SERVICE=false
done

SERVICE_CLEANUP_WARNINGS="false"
if [ ${#DELETE_SERVICE[@]} -ne 0 ]; then
SERVICE_CLEANUP_WARNINGS="true"
((++BUILD_WARNING_COUNT))
echo ">> Lagoon detected services that have been removed from the docker-compose file"
if [ "$(featureFlag CLEANUP_REMOVED_LAGOON_SERVICES)" != enabled ]; then
echo "> You can remove these in the next build by setting the flag 'LAGOON_FEATURE_FLAG_CLEANUP_REMOVED_LAGOON_SERVICES=enabled' as a GLOBAL scoped variable to this environment or project"
fi
for DS in ${DELETE_SERVICE[@]}
do
if [ "$(featureFlag CLEANUP_REMOVED_LAGOON_SERVICES)" = enabled ]; then
echo ">> Removing deployment ${DS}"
if kubectl -n ${NAMESPACE} get deployments ${DS} &> /dev/null; then
kubectl -n ${NAMESPACE} delete deployment ${DS}
fi
if kubectl -n ${NAMESPACE} get service ${DS} &> /dev/null; then
echo ">>> Removing associated service ${DS}"
kubectl -n ${NAMESPACE} delete service ${DS}
fi
if kubectl -n ${NAMESPACE} get ingress ${DS} &> /dev/null; then
echo ">>> Removing associated ingress ${DS}"
kubectl -n ${NAMESPACE} delete ingress ${DS}
fi
if kubectl -n ${NAMESPACE} get pvc ${DS} &> /dev/null; then
echo ">>> Removing associated persistent volume ${DS}"
kubectl -n ${NAMESPACE} delete pvc ${DS}
fi
#delete anything else?
else
echo ">> The deployment '${DS}' would be removed"
if kubectl -n ${NAMESPACE} get service ${DS} &> /dev/null; then
echo ">>> The associated service '${DS}' would be removed"
fi
if kubectl -n ${NAMESPACE} get ingress ${DS} &> /dev/null; then
echo ">>> The associated ingress '${DS}' would be removed"
fi
if kubectl -n ${NAMESPACE} get pvc ${DS} &> /dev/null; then
echo ">>> The associated persistent volume '${DS}' would be removed"
fi
fi
done
else
echo "No service cleanup required"
fi

currentStepEnd="$(date +"%Y-%m-%d %H:%M:%S")"
patchBuildStep "${buildStartTime}" "${previousStepEnd}" "${currentStepEnd}" "${NAMESPACE}" "serviceCleanupComplete" "Service/Deployment Cleanup" "${SERVICE_CLEANUP_WARNINGS}"
previousStepEnd=${currentStepEnd}
beginBuildStep "Cronjob Cleanup" "cleaningUpCronjobs"
set -x

Expand Down

0 comments on commit cf2a8b9

Please sign in to comment.