mirror of
https://github.com/google/nomulus.git
synced 2025-04-29 19:47:51 +02:00
176 lines
5.5 KiB
Go
176 lines
5.5 KiB
Go
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// The cloudScheduler tool allows creating, updating and deleting cloud
|
|
// scheduler jobs from xml config file
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 3 || os.Args[1] == "" || os.Args[2] == "" {
|
|
panic("Error - invalid parameters:\nFirst parameter required - config file path;\nSecond parameter required - project name")
|
|
}
|
|
|
|
// Config file path
|
|
configFileLocation := os.Args[1]
|
|
// Project name where to submit the tasks
|
|
projectName := os.Args[2]
|
|
|
|
log.Default().Println("Filepath " + configFileLocation)
|
|
|
|
xmlFile, err := os.Open(configFileLocation)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer xmlFile.Close()
|
|
|
|
type Task struct {
|
|
XMLName xml.Name `xml:"task"`
|
|
URL string `xml:"url"`
|
|
Description string `xml:"description"`
|
|
Schedule string `xml:"schedule"`
|
|
Name string `xml:"name"`
|
|
}
|
|
|
|
type Taskentries struct {
|
|
XMLName xml.Name `xml:"taskentries"`
|
|
Task []Task `xml:"task"`
|
|
}
|
|
|
|
type ServiceAccount struct {
|
|
DisplayName string `json:"displayName"`
|
|
Email string `json:"email"`
|
|
}
|
|
|
|
type ExistingJob struct {
|
|
Name string `json:"name"`
|
|
State string `json:"state"`
|
|
}
|
|
|
|
byteValue, _ := io.ReadAll(xmlFile)
|
|
|
|
var taskEntries Taskentries
|
|
|
|
if err := xml.Unmarshal(byteValue, &taskEntries); err != nil {
|
|
panic("Failed to unmarshal taskentries: " + err.Error())
|
|
}
|
|
|
|
getArgs := func(taskRecord Task, operationType string, serviceAccountEmail string) []string {
|
|
// Cloud Schedule doesn't allow description of more than 499 chars and \n
|
|
var description string
|
|
if len(taskRecord.Description) > 499 {
|
|
description = taskRecord.Description[:499]
|
|
} else {
|
|
description = taskRecord.Description
|
|
}
|
|
description = strings.ReplaceAll(description, "\n", " ")
|
|
|
|
return []string{
|
|
"--project", projectName,
|
|
"scheduler", "jobs", operationType,
|
|
"http", taskRecord.Name,
|
|
"--location", "us-central1",
|
|
"--schedule", taskRecord.Schedule,
|
|
"--uri", fmt.Sprintf("https://backend-dot-%s.appspot.com%s", projectName, strings.TrimSpace(taskRecord.URL)),
|
|
"--description", description,
|
|
"--http-method", "get",
|
|
"--oidc-service-account-email", serviceAccountEmail,
|
|
"--oidc-token-audience", projectName,
|
|
}
|
|
}
|
|
|
|
// Get existing jobs from Cloud Scheduler
|
|
var allExistingJobs []ExistingJob
|
|
cmdGetExistingList := exec.Command("gcloud", "scheduler", "jobs", "list", "--project="+projectName, "--location=us-central1", "--format=json")
|
|
cmdGetExistingListOutput, cmdGetExistingListError := cmdGetExistingList.CombinedOutput()
|
|
if cmdGetExistingListError != nil {
|
|
panic("Can't obtain existing cloud scheduler jobs for " + projectName)
|
|
}
|
|
err = json.Unmarshal(cmdGetExistingListOutput, &allExistingJobs)
|
|
if err != nil {
|
|
panic("Failed to parse existing jobs from cloud schedule: " + err.Error())
|
|
}
|
|
|
|
// Sync deleted jobs
|
|
enabledExistingJobs := map[string]bool{}
|
|
for i := 0; i < len(allExistingJobs); i++ {
|
|
jobName := strings.Split(allExistingJobs[i].Name, "jobs/")[1]
|
|
toBeDeleted := true
|
|
if allExistingJobs[i].State == "ENABLED" {
|
|
enabledExistingJobs[jobName] = true
|
|
}
|
|
for i := 0; i < len(taskEntries.Task); i++ {
|
|
if taskEntries.Task[i].Name == jobName {
|
|
toBeDeleted = false
|
|
break
|
|
}
|
|
}
|
|
if toBeDeleted {
|
|
cmdDelete := exec.Command("gcloud", "scheduler", "jobs", "delete", jobName, "--project="+projectName, "--quiet")
|
|
cmdDeleteOutput, cmdDeleteError := cmdDelete.CombinedOutput()
|
|
log.Default().Println("Deleting cloud scheduler job " + jobName)
|
|
if cmdDeleteError != nil {
|
|
panic(string(cmdDeleteOutput))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find service account email
|
|
var serviceAccounts []ServiceAccount
|
|
var serviceAccountEmail string
|
|
cmdGetServiceAccounts := exec.Command("gcloud", "iam", "service-accounts", "list", "--project="+projectName, "--format=json")
|
|
cmdGetServiceAccountsOutput, cmdGetServiceAccountsError := cmdGetServiceAccounts.CombinedOutput()
|
|
if cmdGetServiceAccountsError != nil {
|
|
panic(cmdGetServiceAccountsError)
|
|
}
|
|
err = json.Unmarshal(cmdGetServiceAccountsOutput, &serviceAccounts)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
for i := 0; i < len(serviceAccounts); i++ {
|
|
if serviceAccounts[i].DisplayName == "cloud-scheduler" {
|
|
serviceAccountEmail = serviceAccounts[i].Email
|
|
break
|
|
}
|
|
}
|
|
if serviceAccountEmail == "" {
|
|
panic("Service account for cloud scheduler is not created for " + projectName)
|
|
}
|
|
|
|
// Sync created and updated jobs
|
|
for i := 0; i < len(taskEntries.Task); i++ {
|
|
cmdType := "update"
|
|
if enabledExistingJobs[taskEntries.Task[i].Name] != true {
|
|
cmdType = "create"
|
|
}
|
|
|
|
syncCommand := exec.Command("gcloud", getArgs(taskEntries.Task[i], cmdType, serviceAccountEmail)...)
|
|
syncCommandOutput, syncCommandError := syncCommand.CombinedOutput()
|
|
log.Default().Println(cmdType + " cloud scheduler job " + taskEntries.Task[i].Name)
|
|
if syncCommandError != nil {
|
|
panic(string(syncCommandOutput))
|
|
}
|
|
}
|
|
}
|