cmd/relui,internal/relui: create and run workflows

Create and run workflows, storing state in a postgres database. Generate
database queries using sqlc, which allows the author to have complete
control over the query structure.

The basic structure of creating and running a workflow works, but
there is still much work to do in terms of selecting which workflow to
create, recording logs, maintaining a list of currently running
workflows, and recording workflow-level results.

Task persistence needs to be updated, as the current implementation
cannot be resumed due to losing the type during marshalling.

For golang/go#47401

Change-Id: I9ccddf117d023f70568de655c482820a1152d9cb
Reviewed-on: https://go-review.googlesource.com/c/build/+/350449
Trust: Alexander Rakoczy <alex@golang.org>
Run-TryBot: Alexander Rakoczy <alex@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/workflow/workflow.go b/internal/workflow/workflow.go
index 2720a37..016b14e 100644
--- a/internal/workflow/workflow.go
+++ b/internal/workflow/workflow.go
@@ -195,9 +195,9 @@
 type Listener interface {
 	// TaskStateChanged is called when the state of a task changes.
 	// state is safe to store or modify.
-	TaskStateChanged(workflowID, taskID string, state *TaskState) error
+	TaskStateChanged(workflowID uuid.UUID, taskID string, state *TaskState) error
 	// Logger is called to obtain a Logger for a particular task.
-	Logger(workflowID, taskID string) Logger
+	Logger(workflowID uuid.UUID, taskID string) Logger
 }
 
 // TaskState contains the state of a task in a running workflow. Once Finished
@@ -212,7 +212,7 @@
 
 // WorkflowState contains the shallow state of a running workflow.
 type WorkflowState struct {
-	ID     string
+	ID     uuid.UUID
 	Params map[string]string
 }
 
@@ -245,7 +245,7 @@
 
 // A Workflow is an instantiated workflow instance, ready to run.
 type Workflow struct {
-	ID     string
+	ID     uuid.UUID
 	def    *Definition
 	params map[string]string
 
@@ -291,7 +291,7 @@
 // Start instantiates a workflow with the given parameters.
 func Start(def *Definition, params map[string]string) (*Workflow, error) {
 	w := &Workflow{
-		ID:     uuid.New().String(),
+		ID:     uuid.New(),
 		def:    def,
 		params: params,
 		tasks:  map[*taskDefinition]*taskState{},
@@ -438,11 +438,11 @@
 
 type defaultListener struct{}
 
-func (s *defaultListener) TaskStateChanged(_, _ string, _ *TaskState) error {
+func (s *defaultListener) TaskStateChanged(_ uuid.UUID, _ string, _ *TaskState) error {
 	return nil
 }
 
-func (s *defaultListener) Logger(_, task string) Logger {
+func (s *defaultListener) Logger(_ uuid.UUID, task string) Logger {
 	return &defaultLogger{}
 }
 
diff --git a/internal/workflow/workflow_test.go b/internal/workflow/workflow_test.go
index bacf4c1..4044ddf 100644
--- a/internal/workflow/workflow_test.go
+++ b/internal/workflow/workflow_test.go
@@ -16,6 +16,7 @@
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
 	"golang.org/x/build/internal/workflow"
 )
 
@@ -141,7 +142,7 @@
 	logger workflow.Logger
 }
 
-func (l *logTestListener) Logger(_, _ string) workflow.Logger {
+func (l *logTestListener) Logger(_ uuid.UUID, _ string) workflow.Logger {
 	return l.logger
 }
 
@@ -219,12 +220,12 @@
 
 type mapListener struct {
 	workflow.Listener
-	states map[string]map[string]*workflow.TaskState
+	states map[uuid.UUID]map[string]*workflow.TaskState
 }
 
-func (l *mapListener) TaskStateChanged(workflowID, taskID string, state *workflow.TaskState) error {
+func (l *mapListener) TaskStateChanged(workflowID uuid.UUID, taskID string, state *workflow.TaskState) error {
 	if l.states == nil {
-		l.states = map[string]map[string]*workflow.TaskState{}
+		l.states = map[uuid.UUID]map[string]*workflow.TaskState{}
 	}
 	if l.states[workflowID] == nil {
 		l.states[workflowID] = map[string]*workflow.TaskState{}
@@ -262,7 +263,7 @@
 
 type verboseListener struct{ t *testing.T }
 
-func (l *verboseListener) TaskStateChanged(_, _ string, st *workflow.TaskState) error {
+func (l *verboseListener) TaskStateChanged(_ uuid.UUID, _ string, st *workflow.TaskState) error {
 	switch {
 	case !st.Finished:
 		l.t.Logf("task %-10v: started", st.Name)
@@ -274,7 +275,7 @@
 	return nil
 }
 
-func (l *verboseListener) Logger(_, task string) workflow.Logger {
+func (l *verboseListener) Logger(_ uuid.UUID, task string) workflow.Logger {
 	return &testLogger{t: l.t, task: task}
 }