Repository: crossplane-contrib/provider-terraform (go)

Issue:
Limit garbage collection to workspace directories with UUID names
### What happened?
Garbage collection is monitoring all directories under /tf and comparing them to the list of provisioned Workspaces.  Any directory that does not have a valid Workspace UUID is removed.

It would be helpful to be able to use the /tf directory as a provider cache to eliminate the need to duplicate all providers in every workspace.  In order for this to work we need Garbage Collection to not remove the provider cache directories.

### How can we reproduce it?
Create a ControllerConfig for provider-terraform and specify an env variable TF_CACHE_DIR=/tf

Observe that there is a cache directory under /tf called registry.terraform.io and that this directory gets removed when garbage collection runs (the timestamp is updated when it gets recreated by the next terraform init)

This issue tracks a minor enhancement that will keep Garbage Collection from removing any directory that is not a Workspace UUID name.

Pull request description:
Update gc
### Description of your changes
Update Garbage Collection to ignore all directories that are not UUIDs

Fixes #66 

I have:

- [X] Read and followed Crossplane's [contribution process].
- [X] Run `make reviewable test` to ensure this PR is ready for review.

### How has this code been tested
Running in our local dev environment and verified that GC does not remove other directories

[contribution process]: https://git.io/fj2m9


Patch:
diff --git a/go.mod b/go.mod
index 4fc5e7f..936d43c 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	github.com/crossplane/crossplane-runtime v0.14.0
 	github.com/crossplane/crossplane-tools v0.0.0-20201201125637-9ddc70edfd0d
 	github.com/google/go-cmp v0.5.2
+	github.com/google/uuid v1.1.2
 	github.com/hashicorp/go-getter v1.4.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/afero v1.5.1
@@ -39,7 +40,6 @@ require (
 	github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
 	github.com/golang/protobuf v1.4.3 // indirect
 	github.com/google/gofuzz v1.1.0 // indirect
-	github.com/google/uuid v1.1.2 // indirect
 	github.com/googleapis/gax-go/v2 v2.0.5 // indirect
 	github.com/googleapis/gnostic v0.5.1 // indirect
 	github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
diff --git a/internal/workdir/workdir.go b/internal/workdir/workdir.go
index 3866c65..6e59571 100644
--- a/internal/workdir/workdir.go
+++ b/internal/workdir/workdir.go
@@ -22,11 +22,13 @@ import (
 	"strings"
 	"time"
 
-	"github.com/crossplane-contrib/provider-terraform/apis/v1alpha1"
 	"github.com/crossplane/crossplane-runtime/pkg/logging"
+	"github.com/google/uuid"
 	"github.com/pkg/errors"
 	"github.com/spf13/afero"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/crossplane-contrib/provider-terraform/apis/v1alpha1"
 )
 
 // Error strings.
@@ -99,6 +101,11 @@ func (gc *GarbageCollector) Run(ctx context.Context) {
 	}
 }
 
+func isUUID(u string) bool {
+	_, err := uuid.Parse(u)
+	return err == nil
+}
+
 func (gc *GarbageCollector) collect(ctx context.Context) error {
 	l := &v1alpha1.WorkspaceList{}
 	if err := gc.kube.List(ctx, l); err != nil {
@@ -109,7 +116,6 @@ func (gc *GarbageCollector) collect(ctx context.Context) error {
 	for _, ws := range l.Items {
 		exists[string(ws.GetUID())] = true
 	}
-
 	fis, err := gc.fs.ReadDir(gc.parentDir)
 	if err != nil {
 		return errors.Wrapf(err, errFmtReadDir, gc.parentDir)
@@ -117,7 +123,7 @@ func (gc *GarbageCollector) collect(ctx context.Context) error {
 
 	failed := make([]string, 0)
 	for _, fi := range fis {
-		if !fi.IsDir() {
+		if !fi.IsDir() || !isUUID(fi.Name()) {
 			continue
 		}
 		if exists[fi.Name()] {


Test patch:
diff --git a/internal/workdir/workdir_test.go b/internal/workdir/workdir_test.go
index 59e3860..cadd9f3 100644
--- a/internal/workdir/workdir_test.go
+++ b/internal/workdir/workdir_test.go
@@ -102,21 +102,23 @@ func TestCollect(t *testing.T) {
 			fields: fields{
 				kube: &test.MockClient{MockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {
 					*obj.(*v1alpha1.WorkspaceList) = v1alpha1.WorkspaceList{Items: []v1alpha1.Workspace{
-						{ObjectMeta: metav1.ObjectMeta{UID: types.UID("a")}},
-						{ObjectMeta: metav1.ObjectMeta{UID: types.UID("b")}},
+						{ObjectMeta: metav1.ObjectMeta{UID: types.UID("8371dd9e-dd3f-4a42-bd8c-340c4744f6de")}},
+						{ObjectMeta: metav1.ObjectMeta{UID: types.UID("ebaac629-43a3-4b39-8138-d7ac19cafe11")}},
 					}}
 					return nil
 				})},
 				parentdDir: parentDir,
 				fs: withDirs(afero.Afero{Fs: afero.NewMemMapFs()},
 					parentDir,
-					filepath.Join(parentDir, "a"),
-					filepath.Join(parentDir, "b"),
-					filepath.Join(parentDir, "c"),
+					filepath.Join(parentDir, "8371dd9e-dd3f-4a42-bd8c-340c4744f6de"),
+					filepath.Join(parentDir, "ebaac629-43a3-4b39-8138-d7ac19cafe11"),
+					filepath.Join(parentDir, "0d177133-1a2f-4ce2-93d2-f8212d3344e7"),
+					filepath.Join(parentDir, "helm"),
+					filepath.Join(parentDir, "registry.terraform.io"),
 				),
 			},
 			want: want{
-				dirs: []string{"a", "b"},
+				dirs: []string{"8371dd9e-dd3f-4a42-bd8c-340c4744f6de", "ebaac629-43a3-4b39-8138-d7ac19cafe11", "helm", "registry.terraform.io"},
 			},
 		},
 	}