This repository has been archived by the owner on Jan 8, 2024. It is now read-only.
/
operation_poll.go
164 lines (143 loc) · 4.73 KB
/
operation_poll.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package runner
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/waypoint-plugin-sdk/terminal"
pb "github.com/hashicorp/waypoint/pkg/server/gen"
)
func (r *Runner) executePollOp(
ctx context.Context,
log hclog.Logger,
ui terminal.UI,
job *pb.Job,
) (*pb.Job_Result, error) {
sourcer, err := r.dataSourcer(ctx, log, job.DataSource, job.DataSourceOverrides)
if err != nil {
return nil, err
}
// Query this project. We're mainly trying to get all the pb.Workspace_Project
// values for a project so that we can get the data ref that we last polled
// for each project.
log.Trace("calling GetProject to get list of workspaces for project")
resp, err := r.client.GetProject(ctx, &pb.GetProjectRequest{
Project: &pb.Ref_Project{
Project: job.Application.Project,
},
})
if err != nil {
return nil, err
}
// Get the current ref for the default workspace.
//
// NOTE(mitchellh): for now, we only support the default workspace. We
// will expand support to polling non-default workspaces in the future.
log.Trace("finding latest ref")
var currentRef *pb.Job_DataSource_Ref
if resp != nil {
for _, p := range resp.Workspaces {
if p.Workspace.Workspace == "default" {
currentRef = p.DataSourceRef
break
}
}
}
log.Debug("current ref for poll operation", "ref", currentRef)
// Get any changes
newRef, ignore, err := sourcer.Changes(ctx, log, ui, job.DataSource, currentRef, r.tempDir)
if err != nil {
return nil, err
}
log.Debug("result of Changes, nil means no changes", "result", newRef, "ignore", ignore)
// If we have no changes, then we're done.
if newRef == nil {
return &pb.Job_Result{}, nil
}
// Setup our overrides. Overrides are used to set the exact ref that
// the job will use.
overrides, err := sourcer.RefToOverride(newRef)
if err != nil {
return nil, err
}
// Setup our job template. This will be used with the QueueProject operation
// to queue an "up" for each app within the project.
jobTemplate := &pb.Job{
TargetRunner: &pb.Ref_Runner{
Target: &pb.Ref_Runner_Any{
Any: &pb.Ref_RunnerAny{},
},
},
// NOTE(mitchellh): default workspace only for now
Workspace: &pb.Ref_Workspace{Workspace: "default"},
// Reuse the same data source and bring in our overrides to set the ref
DataSource: job.DataSource,
DataSourceOverrides: overrides,
// Doing a plain old "up"
Operation: &pb.Job_Up{
Up: &pb.Job_UpOp{},
},
}
// If we're ignoring, we change the job to a noop job. This will
// still trigger the machinery to update the ref associated with
// the project/app and avoids the poll job from having to have too
// much access or require new APIs to do this.
if ignore {
log.Debug("changes marked as ignorable, scheduling a noop job to update our data ref")
jobTemplate.Operation = &pb.Job_Noop_{
Noop: &pb.Job_Noop{},
}
}
// NOTE(briancain): We set a singleton ID for a poll project operation to ensure that the
// poll handler does not fire off many operations of the same kind more than once,
// clogging up the job system. By setting a singleton ID that is unique to this
// application or project, we can ensure only 1 operation will be active at once rather than
// many operations (such as in the case where a poll interval is shorter than it
// takes to run the operation)
// We assume a project and workspace is set given this is Project polling
singletonId := strings.ToLower(fmt.Sprintf(
"poll-trigger/%s/%s",
job.Workspace.Workspace,
job.Application.Project,
))
// Not all jobs set an application
if job.Application.Application != "" {
singletonId += "/" + strings.ToLower(job.Application.Application)
}
log.Debug("queueing job")
queueResp, err := r.client.QueueJob(ctx, &pb.QueueJobRequest{
Job: &pb.Job{
// We set a singleton ID to verify that we only setup
// a queue operation once (in case it is taking longer to
// process than the poll interval).
SingletonId: singletonId,
// Target only our project, we don't need an app for this.
Application: &pb.Ref_Application{
Project: resp.Project.Name,
},
// Copy all of these fields from the job template since we
// want to execute the same way.
TargetRunner: jobTemplate.TargetRunner,
Workspace: jobTemplate.Workspace,
DataSource: jobTemplate.DataSource,
DataSourceOverrides: jobTemplate.DataSourceOverrides,
// Doing a plain old "up"
Operation: &pb.Job_QueueProject{
QueueProject: &pb.Job_QueueProjectOp{
JobTemplate: jobTemplate,
},
},
},
})
if err != nil {
return nil, err
}
log.Debug("job queued", "job_id", queueResp.JobId)
return &pb.Job_Result{
Poll: &pb.Job_PollResult{
JobId: queueResp.JobId,
OldRef: currentRef,
NewRef: newRef,
},
}, nil
}