Skip to content

feat: Update project cleaner function to clean up Cloud Endpoints #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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