Skip to content

Commit

Permalink
feat: Update project cleaner function to clean up Cloud Endpoints (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
morgante authored Aug 11, 2020
1 parent 909d428 commit 4403dbe
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 17 deletions.
1 change: 1 addition & 0 deletions .kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
driver:
name: "terraform"
command_timeout: 1800
verify_version: false

provisioner:
name: "terraform"
Expand Down
2 changes: 1 addition & 1 deletion build/int.cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ tags:
- 'integration'
substitutions:
_DOCKER_IMAGE_DEVELOPER_TOOLS: 'cft/developer-tools'
_DOCKER_TAG_VERSION_DEVELOPER_TOOLS: '0.6.0'
_DOCKER_TAG_VERSION_DEVELOPER_TOOLS: '0'
8 changes: 7 additions & 1 deletion examples/logs-slack-alerts/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
*/

provider "google-beta" {
version = "~> 2.1"
version = "~> 3.33"
project = var.project_id
region = var.region
}

provider "google" {
version = "~> 3.33"
project = var.project_id
region = var.region
}
Expand Down
8 changes: 7 additions & 1 deletion examples/pubsub_scheduled/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ terraform {
}

provider "google-beta" {
version = "~> 2.5"
version = "~> 3.33"
project = var.project_id
region = var.region
}

provider "google" {
version = "~> 3.33"
project = var.project_id
region = var.region
}
Expand Down
8 changes: 7 additions & 1 deletion examples/pubsub_scheduled_multiple/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ terraform {
}

provider "google-beta" {
version = "~> 2.5"
version = "~> 3.33"
project = var.project_id
region = var.region
}

provider "google" {
version = "~> 3.33"
project = var.project_id
region = var.region
}
Expand Down
2 changes: 1 addition & 1 deletion modules/project_cleanup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The following services must be enabled on the project housing the cleanup functi

| Name | Description | Type | Default | Required |
|------|-------------|:----:|:-----:|:-----:|
| function\_timeout\_s | The amount of time in seconds allotted for the execution of the function. | number | `"60"` | no |
| function\_timeout\_s | The amount of time in seconds allotted for the execution of the function. | number | `"500"` | no |
| job\_schedule | Cleaner function run frequency, in cron syntax | string | `"*/5 * * * *"` | no |
| max\_project\_age\_in\_hours | The maximum number of hours that a GCP project, selected by `target_tag_name` and `target_tag_value`, can exist | number | `"6"` | no |
| organization\_id | The organization ID whose projects to clean up | string | n/a | yes |
Expand Down
4 changes: 2 additions & 2 deletions modules/project_cleanup/function_source/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ The following environment variables may be specified to configure the cleanup ut
| Name | Description | Type | Default | Required |
|------|-------------|:----:|:-----:|:-----:|
| `TARGET_EXCLUDED_LABELS` | Labels to match on for identifying projects to avoid deletion | string | n/a | no |
| `TARGET_FOLDER_ID` | Folder ID to delete prjojects under | string | n/a | yes |
| `TARGET_FOLDER_ID` | Folder ID to delete projects under | string | n/a | yes |
| `TARGET_INCLUDED_LABELS` | Labels to match on for identifying projects to delete | string | n/a | no |
| `MAX_PROJECT_AGE_HOURS` | The project age, in hours, at which point deletion should be considered | integer | n/a | no |
| `MAX_PROJECT_AGE_HOURS` | The project age, in hours, at which point deletion should be considered | integer | n/a | yes |

## Required Permissions

Expand Down
4 changes: 2 additions & 2 deletions modules/project_cleanup/function_source/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/terraform-google-modules/project-cleaner
module github.com/terraform-google-modules/terraform-google-scheduled-function/modules/project_cleanup

go 1.11
go 1.13

require (
golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b
Expand Down
57 changes: 53 additions & 4 deletions modules/project_cleanup/function_source/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package project_cleaner
package project_cleanup

import (
"encoding/json"
Expand All @@ -26,10 +26,12 @@ import (
"strconv"
"strings"
"time"

"golang.org/x/net/context"
"golang.org/x/oauth2/google"
"google.golang.org/api/cloudresourcemanager/v1"
cloudresourcemanager2 "google.golang.org/api/cloudresourcemanager/v2"
"google.golang.org/api/servicemanagement/v1"
)

const (
Expand Down Expand Up @@ -123,6 +125,12 @@ func getLabelsMapFromEnv(envVariableName string) map[string]string {
targetExcludedLabels := os.Getenv(envVariableName)
logger.Println("Try to get labels map")
labels := make(map[string]string)

if targetExcludedLabels == "" {
logger.Printf("No labels provided.")
return nil
}

err := json.Unmarshal([]byte(targetExcludedLabels), &labels)
if err != nil {
logger.Printf("Fail to get labels map from [%s] env variable, error [%s]", envVariableName, err.Error())
Expand All @@ -141,6 +149,14 @@ func getCorrectFolderIdOrTerminateExecution() string {
return targetFolderIdString
}

func getServiceManagementServiceOrTerminateExecution(client *http.Client) *servicemanagement.APIService {
service, err := servicemanagement.New(client)
if err != nil {
logger.Fatalf("Failed to get service management API client with error [%s], terminate execution", err.Error())
}
return service
}

func getResourceManagerServiceOrTerminateExecution(client *http.Client) *cloudresourcemanager.Service {
logger.Println("Try to get Cloud Resource Manager")
cloudResourceManagerService, err := cloudresourcemanager.New(client)
Expand Down Expand Up @@ -175,6 +191,7 @@ func invoke(ctx context.Context) {
client := initializeGoogleClient(ctx)
cloudResourceManagerService := getResourceManagerServiceOrTerminateExecution(client)
folderService := getFolderServiceOrTerminateExecution(client)
endpointService := getServiceManagementServiceOrTerminateExecution(client)

removeLien := func(name string) {
logger.Printf("Try to remove lien [%s]", name)
Expand All @@ -184,12 +201,44 @@ func invoke(ctx context.Context) {
} else {
logger.Printf("Removed lien [%s]", name)
}
}

removeProjectById := func(projectId string) error {
_, err := cloudResourceManagerService.Projects.Delete(projectId).Context(ctx).Do()
return err
}

removeProjectEndpoints := func(projectId string) {
logger.Printf("Try to remove endpoints for [%s]", projectId)
listResponse, err := endpointService.Services.List().ProducerProjectId(projectId).Do()
if err != nil {
logger.Printf("Fail to list services for [%s], error [%s]", projectId, err.Error())
return
}

if len(listResponse.Services) <= 1 {
return
}

for _, service := range listResponse.Services {
logger.Printf("Try to remove service: %s", service.ServiceName)
_, err = endpointService.Services.Delete(service.ServiceName).Do()
if err != nil {
logger.Printf("Fail to delete service [%s] for [%s], error [%s]", service.ServiceName, projectId, err.Error())
}
}

// wait for services to complete deletion
time.Sleep(10 * time.Second)
}

removeProjectById := func(projectId string) {
cleanupProjectById := func(projectId string) {
logger.Printf("Try to remove project [%s]", projectId)
_, err := cloudResourceManagerService.Projects.Delete(projectId).Context(ctx).Do()
err := removeProjectById(projectId)
if err != nil {
removeProjectEndpoints(projectId)
err = removeProjectById(projectId)
}
if err != nil {
logger.Printf("Fail to remove project [%s], error [%s]", projectId, err.Error())
} else {
Expand All @@ -206,7 +255,7 @@ func invoke(ctx context.Context) {
for _, lien := range page.Liens {
removeLien(lien.Name)
}
removeProjectById(projectId)
cleanupProjectById(projectId)
return nil
}); err != nil {
logger.Printf("Fail to get all liens for the project [%s], error [%s]", projectId, err.Error())
Expand Down
11 changes: 8 additions & 3 deletions modules/project_cleanup/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ resource "google_service_account" "project_cleaner_function" {
}

resource "google_organization_iam_member" "main" {
for_each = toset(["projectDeleter", "folderViewer", "lienModifier"])
for_each = toset([
"roles/resourcemanager.projectDeleter",
"roles/resourcemanager.folderViewer",
"roles/resourcemanager.lienModifier",
"roles/owner"
])

member = "serviceAccount:${google_service_account.project_cleaner_function.email}"
org_id = var.organization_id
role = "roles/resourcemanager.${each.value}"
role = each.value
}

module "scheduled_project_cleaner" {
Expand All @@ -44,7 +49,7 @@ module "scheduled_project_cleaner" {
topic_name = var.topic_name
function_available_memory_mb = 128
function_description = "Clean up GCP projects older than ${var.max_project_age_in_hours} hours matching particular tags"
function_runtime = "go111"
function_runtime = "go113"
function_service_account_email = google_service_account.project_cleaner_function.email
function_timeout_s = var.function_timeout_s

Expand Down
2 changes: 1 addition & 1 deletion modules/project_cleanup/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

variable "function_timeout_s" {
type = number
default = 60
default = 500
description = "The amount of time in seconds allotted for the execution of the function."
}

Expand Down

0 comments on commit 4403dbe

Please sign in to comment.