/
uiservice.go
159 lines (138 loc) · 3.94 KB
/
uiservice.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
package werft
import (
"context"
"path/filepath"
"strings"
"sync"
"time"
"github.com/csweichel/werft/pkg/api/repoconfig"
v1 "github.com/csweichel/werft/pkg/api/v1"
"github.com/csweichel/werft/pkg/reporef"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
// UIService implements api/v1/WerftUIServer
type UIService struct {
RepositoryProvider RepositoryProvider
Repos []string
Readonly bool
cache []*v1.ListJobSpecsResponse
mu sync.RWMutex
}
// NewUIService produces a new UI service and initializes its repo list
func NewUIService(repoprov RepositoryProvider, repos []string, readonly bool, updateInterval time.Duration) (*UIService, error) {
r := &UIService{
RepositoryProvider: repoprov,
Repos: repos,
Readonly: readonly,
}
err := r.updateJobSpecs()
if err != nil {
return nil, err
}
go func() {
t := time.NewTicker(updateInterval)
for range t.C {
err := r.updateJobSpecs()
if err != nil {
log.WithError(err).Error("cannot update job specs")
}
}
}()
return r, nil
}
// updateJobSpecs updates the cached job spec responses by looking into the configured repositories
func (uis *UIService) updateJobSpecs() error {
uis.mu.Lock()
defer uis.mu.Unlock()
uis.cache = nil
for _, r := range uis.Repos {
repo, err := reporef.Parse(r)
if err != nil {
log.WithError(err).WithField("repo", r).Warn("unable to download job spec while updating UI")
continue
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
err = uis.RepositoryProvider.Resolve(ctx, repo)
if err != nil {
cancel()
log.WithError(err).WithField("repo", r).Warn("unable to download job spec while updating UI")
continue
}
fp, err := uis.RepositoryProvider.FileProvider(ctx, repo)
if err != nil {
cancel()
log.WithError(err).WithField("repo", r).Warn("unable to download job spec while updating UI")
continue
}
paths, err := fp.ListFiles(ctx, ".werft")
if err != nil {
cancel()
log.WithError(err).WithField("repo", r).Warn("unable to download job spec while updating UI")
continue
}
cancel()
for _, fn := range paths {
if !strings.HasSuffix(fn, "yaml") || fn == PathWerftConfig {
continue
}
jobName := strings.TrimSuffix(fn, filepath.Ext(fn))
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
fc, err := fp.Download(ctx, fn)
cancel()
if err != nil {
log.WithError(err).WithField("repo", repo).WithField("path", fn).Warn("unable to download job spec while updating UI")
continue
}
var jobspec repoconfig.JobSpec
err = yaml.NewDecoder(fc).Decode(&jobspec)
fc.Close()
if err != nil {
log.WithError(err).WithField("repo", repo).WithField("path", fn).Warn("unable to unmarshal job spec while updating UI")
continue
}
var args []*v1.DesiredAnnotation
for _, arg := range jobspec.Args {
args = append(args, &v1.DesiredAnnotation{
Name: arg.Name,
Required: arg.Req,
Description: arg.Desc,
})
}
res := &v1.ListJobSpecsResponse{
Repo: &v1.Repository{
Host: "github.com",
Owner: repo.Owner,
Repo: repo.Repo,
Ref: repo.Ref,
Revision: repo.Revision,
},
Name: jobName,
Path: fn,
Description: jobspec.Desc,
Arguments: args,
Plugins: jobspec.Plugins,
}
uis.cache = append(uis.cache, res)
}
}
return nil
}
// ListJobSpecs returns a list of jobs that can be started through the UI.
func (uis *UIService) ListJobSpecs(req *v1.ListJobSpecsRequest, srv v1.WerftUI_ListJobSpecsServer) error {
uis.mu.RLock()
defer uis.mu.RUnlock()
for _, r := range uis.cache {
err := srv.Send(r)
if err != nil {
return err
}
}
return nil
}
// IsReadOnly returns true if the UI is readonly.
func (uis *UIService) IsReadOnly(context.Context, *v1.IsReadOnlyRequest) (*v1.IsReadOnlyResponse, error) {
return &v1.IsReadOnlyResponse{
Readonly: uis.Readonly,
}, nil
}