Skip to content

Commit

Permalink
Create gRPC server and client for dynamic reaper configuration (#8)
Browse files Browse the repository at this point in the history
* Add client and resource files and respective build files

* Add gazelle prefix

* Add client interface and stub for GCE client

* Add gcp client interface and gce client

* Change proto package

* Updated dependencies

* Create GCP client for GCE

* Add files to help with testing

* Create outline for unit tests

* Update build file for test dependencies

* Add regex filtering

* Create framework for testing get resources

* Add test cases for get resources for GCE client

* Add delete resource test, and begin documenting test file

* Add documentation, update build files and delete unnecessary test files

* Fix tiny indentenation mistake

* Add client and resource files and respective build files

* Add gazelle prefix

* Add client interface and stub for GCE client

* Add gcp client interface and gce client

* Updated dependencies

* Create GCP client for GCE

* Add files to help with testing

* Create outline for unit tests

* Update build file for test dependencies

* Add regex filtering

* Create framework for testing get resources

* Add test cases for get resources for GCE client

* Add delete resource test, and begin documenting test file

* Add documentation, update build files and delete unnecessary test files

* Fix tiny indentenation mistake

* Change indententation again

* Begin outlining Reaper logic

* Continue work on Reaper

* Pass context as Auth parameter

* Add test for name and skip filtering

* Add copyright info to resources test file

* Updated go module files

* Fixed bug in test file

* Clean up test resources file

* Fix overengineering of error handling and logging in reaper

* Begin work on reaper unit tests

* Continue adding to reaper tests

* Add test case for checking TTL of watched resource

* Add test logic for update reaper

* Add test cases for reaper update

* Add test logic for reaper run

* Add mock clock to WatchedResource for testing

* Configure reaper tests to run with mock clock

* Add more test cases for reaper

* Remove test files and comments

* Clean up code, update bazel files, and add documentation

* Fix deleted comment

* Refactor everything to use pointers

* Add logging for when resource is deleted

* Add integration test outline

* Work on integration test

* Update gitignore

* Add methods to help build protos

* Work on handling duplicate watched resources

* Handle duplicate watched resources

* Restructure integration test and do some cleaning up

* Fix small bugs

* Update build files and add documentation

* Create reaper manager

* Refactor reaper and finish manager logic

* Add unit test for running reaper on schedule

* Add reaper manager integration test

* Refactor reaper to allow for updating watching resources

* Add checks to integration test

* Refactor update config to not make any web calls, and give that functionality to GetResources

* Update tests for reaper refactor

* Add documentation

* Add delete and list methods to reaper manager

* Fix logging bug

* Add framework for testing reaper manager

* Remove skip filter from reaperconfig proto

* Add reaper manager tests

* Refactor manager and add test for monitoring reapers

* Deleted unnecessary testing files

* Add gcs client, and implement client interface

* Add unit test for auth and delete for gcs client

* Begin adding support for monitoring GCS buckets and objects separately

* Finish GCS client and begin work on tests

* Add GCS to factory method, and refactor GCE for consistant project structure

* Return error instead of log.Fatal

* Return error when parsing schedule

* Begin working on gRPC server

* Continue work on grpc interface

* Finish grpc server

* Finish gRPC server and client

* Add command line arguement for client

* Finish client command line tool

* Add unit tests, fix concurency bugs

* Fix minor bugs, add documentation, clean up code

* Fix build files

* Modified test

* Fix mistake in BUILD file

* Add documentation to client gRPC tools

* Add documentation to proto

* Fix trimming mistake, and syntax error

* Add terminal prompt for uuid when deleting reaper

Co-authored-by: Brian <bmodel@google.com>
  • Loading branch information
brianmodel and Brian authored Jul 21, 2020
1 parent 2abe092 commit 1e84ba3
Show file tree
Hide file tree
Showing 14 changed files with 740 additions and 37 deletions.
13 changes: 13 additions & 0 deletions client/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["client.go"],
importpath = "github.com/googleinterns/cloudai-gcp-test-resource-reaper/client",
visibility = ["//visibility:public"],
deps = [
"//proto:go_default_library",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@org_golang_google_grpc//:go_default_library",
],
)
97 changes: 97 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2020 Google LLC
//
// 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
//
// https://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.

package client

import (
"context"
"fmt"
"log"

"github.com/golang/protobuf/ptypes/empty"
"github.com/googleinterns/cloudai-gcp-test-resource-reaper/reaperconfig"
"google.golang.org/grpc"
)

// ReaperClient is a gRPC client for communicating with the reaper manager server.
type ReaperClient struct {
client reaperconfig.ReaperManagerClient
conn *grpc.ClientConn
ctx context.Context
}

// StartClient returns a client with a gRPC connection with the server running on address:port.
func StartClient(ctx context.Context, address, port string) *ReaperClient {
conn, err := grpc.Dial(fmt.Sprintf("%s:%s", address, port), grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
reaperManagerClient := reaperconfig.NewReaperManagerClient(conn)

return &ReaperClient{
client: reaperManagerClient,
conn: conn,
ctx: ctx,
}
}

// AddReaper adds a new reaper the reaper manager.
func (c *ReaperClient) AddReaper(config *reaperconfig.ReaperConfig) (string, error) {
res, err := c.client.AddReaper(c.ctx, config)
return res.Uuid, err
}

// UpdateReaper updates the reaper from the given ReaperConfig. If the UUID in the config does not exist,
// an error will be thrown.
func (c *ReaperClient) UpdateReaper(config *reaperconfig.ReaperConfig) (string, error) {
res, err := c.client.UpdateReaper(c.ctx, config)
return res.Uuid, err
}

// DeleteReaper deletes the reaper with the given UUID.
func (c *ReaperClient) DeleteReaper(uuid string) error {
_, err := c.client.DeleteReaper(c.ctx, &reaperconfig.Reaper{Uuid: uuid})
return err
}

// ListRunningReapers returns a list of the running reapers' UUIDs.
func (c *ReaperClient) ListRunningReapers() ([]string, error) {
res, err := c.client.ListRunningReapers(c.ctx, new(empty.Empty))
if err != nil {
return nil, err
}
var runningReapers []string
for _, reaper := range res.Reapers {
runningReapers = append(runningReapers, reaper.Uuid)
}
return runningReapers, nil
}

// StartManager starts running the reaper manager. Note this is different from starting the gRPC
// server.
func (c *ReaperClient) StartManager() error {
_, err := c.client.StartManager(c.ctx, new(empty.Empty))
return err
}

// ShutdownManager stops the reaper manager process. Note this does not shut down the gRPC server.
func (c *ReaperClient) ShutdownManager() error {
_, err := c.client.ShutdownManager(c.ctx, new(empty.Empty))
return err
}

// Close closes the ReaperClient connection to the gRPC server.
func (c *ReaperClient) Close() {
c.conn.Close()
}
19 changes: 19 additions & 0 deletions cmd/reaper/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/googleinterns/cloudai-gcp-test-resource-reaper/cmd/reaper",
visibility = ["//visibility:private"],
deps = [
"//client:go_default_library",
"//pkg/reaper:go_default_library",
"//proto:go_default_library",
],
)

go_binary(
name = "reaper",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
211 changes: 211 additions & 0 deletions cmd/reaper/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2020 Google LLC
//
// 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
//
// https://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.

package main

import (
"bufio"
"context"
"flag"
"fmt"
"log"
"os"
"strings"

"github.com/googleinterns/cloudai-gcp-test-resource-reaper/client"
"github.com/googleinterns/cloudai-gcp-test-resource-reaper/pkg/reaper"
"github.com/googleinterns/cloudai-gcp-test-resource-reaper/reaperconfig"
)

func main() {
deleteCmd := flag.NewFlagSet("delete", flag.ExitOnError)
deleteUUID := deleteCmd.String("uuid", "", "UUID of the reaper")

if len(os.Args) < 2 {
fmt.Println("expected 'create', 'update', 'list', 'delete', 'start', or 'shutdown' commands")
os.Exit(1)
}

reaperClient := client.StartClient(context.Background(), "localhost", "8000")
defer reaperClient.Close()

switch os.Args[1] {
case "create":
config, err := createReaperConfigPrompt()
if err != nil {
fmt.Println("Creating reaper config failed with the following error: ", err.Error())
os.Exit(1)
}
uuid, err := reaperClient.AddReaper(config)
if err != nil {
fmt.Println("Create reaper failed with following error: ", err.Error())
os.Exit(1)
}
fmt.Printf("Reaper with UUID %s successfully created\n", uuid)

case "update":
config, err := createReaperConfigPrompt()
if err != nil {
fmt.Println("Creating reaper config failed with the following error: ", err.Error())
os.Exit(1)
}
uuid, err := reaperClient.UpdateReaper(config)
if err != nil {
fmt.Println("Create reaper failed with following error: ", err.Error())
os.Exit(1)
}
fmt.Printf("Reaper with UUID %s successfully updated\n", uuid)

case "list":
reapers, err := reaperClient.ListRunningReapers()
if err != nil {
fmt.Println("List reapers failed with following error: ", err.Error())
os.Exit(1)
}
fmt.Println("Running Reaper UUIDs: ", strings.Join(reapers, ", "))

case "delete":
deleteCmd.Parse(os.Args[2:])
if len(*deleteUUID) == 0 {
var err error
reader := bufio.NewReader(os.Stdin)
fmt.Print("Reaper UUID: ")
*deleteUUID, err = reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
*deleteUUID = strings.TrimSuffix(*deleteUUID, "\n")
}
err := reaperClient.DeleteReaper(*deleteUUID)
if err != nil {
fmt.Println("Delete reapers failed with following error: ", err.Error())
os.Exit(1)
}
fmt.Printf("Reaper with UUID %s successfully deleted\n", *deleteUUID)

case "start":
err := reaperClient.StartManager()
if err != nil {
fmt.Println("Start manager failed with following error: ", err.Error())
}
fmt.Println("Reaper manager started")

case "shutdown":
err := reaperClient.ShutdownManager()
if err != nil {
fmt.Println("Shutdown manager failed with following error: ", err.Error())
}
fmt.Println("Reaper manager shutdown")

default:
fmt.Println("expected 'create', 'update', 'list', 'delete', 'start', or 'shutdown' commands")
os.Exit(1)
}
}

// createReaperConfigPrompt is a command line prompt that walks the user through creating
// a new reaper config.
func createReaperConfigPrompt() (*reaperconfig.ReaperConfig, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Println("Reaper Configuration Setup")

fmt.Print("Reaper UUID: ")
uuid, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
uuid = strings.TrimSuffix(uuid, "\n")

fmt.Print("Project ID: ")
projectID, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
projectID = strings.TrimSuffix(projectID, "\n")

fmt.Print("Reaper run schedule (in cron time string format): ")
schedule, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
schedule = strings.TrimSuffix(schedule, "\n")

var resources []*reaperconfig.ResourceConfig
for {
fmt.Print("Add another resource? (y/n): ")
response, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
if len(response) == 1 || response[0] == 'n' {
break
}

fmt.Print("Resource type: ")
resourceTypeString, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
resourceTypeString = strings.TrimSuffix(resourceTypeString, "\n")
var resourceType reaperconfig.ResourceType

switch resourceTypeString {
case "GCE_VM":
resourceType = reaperconfig.ResourceType_GCE_VM
case "GCS_Bucket":
resourceType = reaperconfig.ResourceType_GCS_BUCKET
case "GCS_Object":
resourceType = reaperconfig.ResourceType_GCS_OBJECT
default:
return nil, fmt.Errorf("Invalid resource type %s", resourceTypeString)
}

fmt.Print("Zones (comma separated list): ")
zonesString, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
zonesString = strings.TrimSuffix(zonesString, "\n")
zones := strings.Split(zonesString, ",")

fmt.Print("Name filter: ")
nameFilter, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
nameFilter = strings.TrimSuffix(nameFilter, "\n")

fmt.Print("Skip filter: ")
skipFilter, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
skipFilter = strings.TrimSuffix(skipFilter, "\n")

fmt.Print("TTL (in cron time string format): ")
ttl, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
ttl = strings.TrimSuffix(ttl, "\n")

resources = append(
resources,
reaper.NewResourceConfig(resourceType, zones, nameFilter, skipFilter, ttl),
)
}

config := reaper.NewReaperConfig(resources, schedule, projectID, uuid)
return config, nil
}
15 changes: 15 additions & 0 deletions cmd/start_server/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/googleinterns/cloudai-gcp-test-resource-reaper/cmd/start_server",
visibility = ["//visibility:private"],
deps = ["//pkg/manager:go_default_library"],
)

go_binary(
name = "start_server",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
23 changes: 23 additions & 0 deletions cmd/start_server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2020 Google LLC
//
// 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
//
// https://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.

package main

import (
"github.com/googleinterns/cloudai-gcp-test-resource-reaper/pkg/manager"
)

func main() {
manager.StartServer("localhost", "8000")
}
Loading

0 comments on commit 1e84ba3

Please sign in to comment.