Skip to content

Commit 6e4a514

Browse files
committed
server+service+database: add bundle trigger endpoint (with authz)
Some arbitrary choices here that we'll need to discuss before merging! Signed-off-by: Stephan Renatus <[email protected]>
1 parent 7ffba34 commit 6e4a514

File tree

8 files changed

+114
-39
lines changed

8 files changed

+114
-39
lines changed

cmd/run/run.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package cmd
22

33
import (
44
"os"
5-
"os/signal"
6-
"syscall"
75

86
"github.com/open-policy-agent/opa-control-plane/cmd"
97
"github.com/open-policy-agent/opa-control-plane/cmd/internal/flags"
@@ -72,12 +70,14 @@ func init() {
7270
log.Fatalf("initialize service: %v", err)
7371
}
7472

75-
// NB(sr): Preliminary, not necessarily something we'll want to keep:
76-
// Rebuild all bundles on SIGHUP.
77-
signalTrigger(svc, log)
78-
7973
go func() {
80-
if err := server.New().WithDatabase(svc.Database()).WithReadiness(svc.Ready).WithConfig(config.Service).Init().ListenAndServe(params.addr); err != nil {
74+
if err := server.New().
75+
WithService(svc).
76+
WithConfig(config.Service).
77+
WithDatabase(svc.Database()).
78+
WithReadiness(svc.Ready).
79+
Init().
80+
ListenAndServe(params.addr); err != nil {
8181
log.Fatalf("failed to start server: %v", err)
8282
}
8383
}()
@@ -100,15 +100,3 @@ func init() {
100100
run,
101101
)
102102
}
103-
104-
func signalTrigger(s *service.Service, l *logging.Logger) {
105-
sigs := make(chan os.Signal, 1)
106-
signal.Notify(sigs, syscall.SIGHUP)
107-
go func() {
108-
for range sigs {
109-
if err := s.TriggerAll(context.Background()); err != nil {
110-
l.Error(err.Error())
111-
}
112-
}
113-
}()
114-
}

e2e/cli/run_with_trigger.txtar

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
! exec $OPACTL run --config config.d/bundle.yml --data-dir tmp --addr 127.0.0.1:8284 --log-level debug &opactl&
2+
! stderr .
3+
! stdout .
4+
5+
exec curl --retry 5 --retry-all-errors http://127.0.0.1:8284/health
6+
7+
# automation token
8+
exec curl -H 'Authorization: bearer sesame' http://127.0.0.1:8284/v1/bundles/hello-world -XPOST
9+
10+
# admin token
11+
exec curl -H 'Authorization: bearer admin' http://127.0.0.1:8284/v1/bundles/hello-world -XPOST
12+
13+
kill opactl
14+
wait opactl
15+
stderr 'triggered bundle build for hello-world'
16+
17+
-- data/common.json --
18+
{ "common": true }
19+
-- config.d/bundle.yml --
20+
bundles:
21+
hello-world:
22+
object_storage:
23+
filesystem:
24+
path: bundles/hello-world/bundle.tar.gz
25+
requirements:
26+
- source: global-data
27+
sources:
28+
global-data:
29+
paths:
30+
- data/common.json
31+
service:
32+
bundle_rebuild_interval: 1h
33+
reconfiguration_interval: 1h
34+
tokens:
35+
admin-user:
36+
api_key: admin
37+
scopes:
38+
- role: administrator
39+
trigger-token:
40+
api_key: sesame
41+
scopes:
42+
- role: automation

internal/authz/authz.rego

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,9 @@ allow if {
4949
data.resource_permissions.principal_id == input.principal
5050
data.resource_permissions.permission == input.permission
5151
}
52+
53+
allow if {
54+
data.principals.id == input.principal
55+
data.principals.role == "automation"
56+
input.permission == "bundles.trigger"
57+
}

internal/config/config.go

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ func (t *Token) Equal(other *Token) bool {
736736
}
737737

738738
type Scope struct {
739-
Role string `json:"role" yaml:"role" enum:"administrator,viewer,owner,stack_owner"`
739+
Role string `json:"role" yaml:"role" enum:"administrator,viewer,owner,stack_owner,automation"`
740740
}
741741

742742
func scopesEqual(a, b []Scope) bool {
@@ -963,18 +963,6 @@ func (a Datasources) Equal(b Datasources) bool {
963963
return setEqual(a, b, func(ds Datasource) string { return ds.Name }, func(a, b Datasource) bool { return a.Equal(&b) })
964964
}
965965

966-
type Service struct {
967-
// ReconfigurationInterval is the duration between configuration checks, i.e. when a change
968-
// to a bundle/stack/source will have an effect on the internal bundle workers.
969-
// String of a duration, e.g. "1m". Defaults to "15s".
970-
ReconfigurationInterval *time.Duration `json:"reconfiguration_interval,omitempty" yaml:"reconfiguration_interval,omitempty"`
971-
972-
// BundleRebuildInterval is the time between bundle builds: After a bundle build as finished,
973-
// OCP will wait _this long_ until it's build again (unless the bundle build is triggered by
974-
// other means). String duration, e.g. "90s". Defaults to "30s".
975-
BundleRebuildInterval *time.Duration `json:"bundle_rebuild_interval,omitempty" yaml:"bundle_rebuild_interval,omitempty"`
976-
}
977-
978966
type Database struct {
979967
SQL *SQLDatabase `json:"sql,omitempty" yaml:"sql,omitempty"`
980968
AWSRDS *AmazonRDS `json:"aws_rds,omitempty" yaml:"aws_rds,omitempty"`
@@ -1003,6 +991,16 @@ type Service struct {
1003991
// ApiPrefix prefixes all endpoints (including health and metrics) with its value. It is important to start with `/` and not end with `/`.
1004992
// For example `/my/path` will make health endpoint be accessible under `/my/path/health`
1005993
ApiPrefix string `json:"api_prefix,omitempty" yaml:"api_prefix,omitempty" pattern:"^/([^/].*[^/])?$"`
994+
995+
// ReconfigurationInterval is the duration between configuration checks, i.e. when a change
996+
// to a bundle/stack/source will have an effect on the internal bundle workers.
997+
// String of a duration, e.g. "1m". Defaults to "15s".
998+
ReconfigurationInterval *time.Duration `json:"reconfiguration_interval,omitempty" yaml:"reconfiguration_interval,omitempty"`
999+
1000+
// BundleRebuildInterval is the time between bundle builds: After a bundle build as finished,
1001+
// OCP will wait _this long_ until it's build again (unless the bundle build is triggered by
1002+
// other means). String duration, e.g. "90s". Defaults to "30s".
1003+
BundleRebuildInterval *time.Duration `json:"bundle_rebuild_interval,omitempty" yaml:"bundle_rebuild_interval,omitempty"`
10061004
}
10071005

10081006
func setEqual[K comparable, V any](a, b []V, key func(V) K, eq func(a, b V) bool) bool {

internal/database/database.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ func (d *Database) sourcesDataPut(ctx context.Context, sourceName, path string,
415415
Name: sourceName,
416416
})
417417
if !allowed {
418-
return errors.New("unauthorized")
418+
return ErrNotAuthorized
419419
}
420420

421421
bs, err := json.Marshal(data)
@@ -1632,6 +1632,15 @@ func (d *Database) deleteNotIn(ctx context.Context, tx *sql.Tx, table, keyColumn
16321632
return err
16331633
}
16341634

1635+
func (d *Database) Check(ctx context.Context, a authz.Access) error {
1636+
return tx1(ctx, d, func(tx *sql.Tx) error {
1637+
if !authz.Check(ctx, tx, d.arg, a) {
1638+
return ErrNotAuthorized
1639+
}
1640+
return nil
1641+
})
1642+
}
1643+
16351644
func (d *Database) arg(i int) string {
16361645
if d.kind == postgres {
16371646
return "$" + strconv.Itoa(i+1)

internal/server/server.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import (
1919
"github.com/open-policy-agent/opa-control-plane/internal/metrics"
2020
"github.com/open-policy-agent/opa-control-plane/internal/server/chain"
2121
"github.com/open-policy-agent/opa-control-plane/internal/server/types"
22+
"github.com/open-policy-agent/opa-control-plane/internal/service"
2223
)
2324

2425
type Server struct {
2526
router *http.ServeMux
2627
db *database.Database
28+
svc *service.Service
2729
readyFn func(context.Context) error
2830
apiPrefix string
2931
}
@@ -62,6 +64,7 @@ func (s *Server) Init() *Server {
6264
setup("GET", "/v1/bundles/{bundle}", s.v1BundlesGet)
6365
setup("PUT", "/v1/bundles/{bundle}", s.v1BundlesPut)
6466
setup("DELETE", "/v1/bundles/{bundle}", s.v1BundlesDelete)
67+
setup("POST", "/v1/bundles/{bundle}", s.v1BundlesPost)
6568

6669
setup("GET", "/v1/stacks", s.v1StacksList)
6770
setup("GET", "/v1/stacks/{stack}", s.v1StacksGet)
@@ -81,6 +84,11 @@ func (s *Server) WithRouter(router *http.ServeMux) *Server {
8184
return s
8285
}
8386

87+
func (s *Server) WithService(svc *service.Service) *Server {
88+
s.svc = svc
89+
return s
90+
}
91+
8492
func (s *Server) WithDatabase(db *database.Database) *Server {
8593
s.db = db
8694
return s
@@ -179,6 +187,24 @@ func (s *Server) v1BundlesGet(w http.ResponseWriter, r *http.Request) {
179187
JSONOK(w, resp, pretty(r))
180188
}
181189

190+
func (s *Server) v1BundlesPost(w http.ResponseWriter, r *http.Request) {
191+
ctx := r.Context()
192+
193+
name, err := url.PathUnescape(r.PathValue("bundle"))
194+
if err != nil {
195+
ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err)
196+
return
197+
}
198+
199+
if err := s.svc.Trigger(ctx, s.auth(r), name); err != nil {
200+
errorAuto(w, err)
201+
return
202+
}
203+
204+
resp := types.BundlesPostResponseV1{}
205+
JSONOK(w, resp, pretty(r))
206+
}
207+
182208
func (s *Server) v1BundlesDelete(w http.ResponseWriter, r *http.Request) {
183209
ctx := r.Context()
184210

internal/server/types/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ type BundlesGetResponseV1 struct {
4545

4646
type BundlesPutResponseV1 struct{}
4747

48+
type BundlesPostResponseV1 struct{}
49+
4850
type BundlesDeleteResponseV1 struct{}
4951

5052
type SourcesGetResponseV1 struct {

internal/service/service.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"sync"
1616
"time"
1717

18+
"github.com/open-policy-agent/opa-control-plane/internal/authz"
1819
"github.com/open-policy-agent/opa-control-plane/internal/builder"
1920
"github.com/open-policy-agent/opa-control-plane/internal/config"
2021
"github.com/open-policy-agent/opa-control-plane/internal/database"
@@ -220,14 +221,17 @@ func (s *Service) Report() *Report {
220221
return s.report
221222
}
222223

223-
func (s *Service) TriggerAll(ctx context.Context) error {
224-
for name := range s.workers {
225-
return s.Trigger(ctx, name)
224+
func (s *Service) Trigger(ctx context.Context, principal, name string) error {
225+
a := authz.Access{
226+
Principal: principal,
227+
Resource: "bundles",
228+
Permission: "bundles.trigger",
229+
Name: name,
230+
}
231+
if err := s.database.Check(ctx, a); err != nil {
232+
return err
226233
}
227-
return nil
228-
}
229234

230-
func (s *Service) Trigger(_ context.Context, name string) error {
231235
err := s.pool.Trigger(name)
232236
if err != nil {
233237
s.log.Errorf("trigger bundle build for %s: %v", name, err)

0 commit comments

Comments
 (0)