Skip to content

Commit f3e7408

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 <stephan.renatus@gmail.com>
1 parent afffedd commit f3e7408

File tree

8 files changed

+103
-27
lines changed

8 files changed

+103
-27
lines changed

cmd/run/run.go

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"bytes"
55
"context"
66
"os"
7-
"os/signal"
8-
"syscall"
97

108
"github.com/spf13/cobra"
119
"github.com/styrainc/opa-control-plane/cmd"
@@ -75,12 +73,13 @@ func init() {
7573
log.Fatalf("initialize service: %v", err)
7674
}
7775

78-
// NB(sr): Preliminary, not necessarily something we'll want to keep:
79-
// Rebuild all bundles on SIGHUP.
80-
signalTrigger(svc, log)
81-
8276
go func() {
83-
if err := server.New().WithDatabase(svc.Database()).WithReadiness(svc.Ready).Init().ListenAndServe(params.addr); err != nil {
77+
if err := server.New().
78+
WithService(svc).
79+
WithDatabase(svc.Database()).
80+
WithReadiness(svc.Ready).
81+
Init().
82+
ListenAndServe(params.addr); err != nil {
8483
log.Fatalf("failed to start server: %v", err)
8584
}
8685
}()
@@ -103,15 +102,3 @@ func init() {
103102
run,
104103
)
105104
}
106-
107-
func signalTrigger(s *service.Service, l *logging.Logger) {
108-
sigs := make(chan os.Signal, 1)
109-
signal.Notify(sigs, syscall.SIGHUP)
110-
go func() {
111-
for range sigs {
112-
if err := s.TriggerAll(context.Background()); err != nil {
113-
l.Error(err.Error())
114-
}
115-
}
116-
}()
117-
}

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: 1 addition & 1 deletion
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 {

internal/database/database.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ func (d *Database) SourcesDataPut(ctx context.Context, sourceName, path string,
401401
Name: sourceName,
402402
})
403403
if !allowed {
404-
return errors.New("unauthorized")
404+
return ErrNotAuthorized
405405
}
406406

407407
bs, err := json.Marshal(data)
@@ -1613,6 +1613,15 @@ func (d *Database) deleteNotIn(ctx context.Context, tx *sql.Tx, table, keyColumn
16131613
return err
16141614
}
16151615

1616+
func (d *Database) Check(ctx context.Context, a authz.Access) error {
1617+
return tx1(ctx, d, func(tx *sql.Tx) error {
1618+
if !authz.Check(ctx, tx, d.arg, a) {
1619+
return ErrNotAuthorized
1620+
}
1621+
return nil
1622+
})
1623+
}
1624+
16161625
func (d *Database) arg(i int) string {
16171626
if d.kind == postgres {
16181627
return "$" + strconv.Itoa(i+1)

internal/server/server.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ import (
1717
"github.com/styrainc/opa-control-plane/internal/database"
1818
"github.com/styrainc/opa-control-plane/internal/metrics"
1919
"github.com/styrainc/opa-control-plane/internal/server/types"
20+
"github.com/styrainc/opa-control-plane/internal/service"
2021
)
2122

2223
type Server struct {
2324
router *http.ServeMux
2425
db *database.Database
26+
svc *service.Service
2527
readyFn func(context.Context) error
2628
}
2729

@@ -48,6 +50,7 @@ func (s *Server) Init() *Server {
4850
s.router.HandleFunc("PUT /v1/bundles/{bundle}", authenticationMiddleware(s.db, s.v1BundlesPut))
4951
s.router.HandleFunc("GET /v1/bundles/{bundle}", authenticationMiddleware(s.db, s.v1BundlesGet))
5052
s.router.HandleFunc("DELETE /v1/bundles/{bundle}", authenticationMiddleware(s.db, s.v1BundlesDelete))
53+
s.router.HandleFunc("POST /v1/bundles/{bundle}", authenticationMiddleware(s.db, s.v1BundlesPost))
5154
s.router.HandleFunc("GET /v1/stacks", authenticationMiddleware(s.db, s.v1StacksList))
5255
s.router.HandleFunc("PUT /v1/stacks/{stack}", authenticationMiddleware(s.db, s.v1StacksPut))
5356
s.router.HandleFunc("GET /v1/stacks/{stack}", authenticationMiddleware(s.db, s.v1StacksGet))
@@ -65,6 +68,11 @@ func (s *Server) WithRouter(router *http.ServeMux) *Server {
6568
return s
6669
}
6770

71+
func (s *Server) WithService(svc *service.Service) *Server {
72+
s.svc = svc
73+
return s
74+
}
75+
6876
func (s *Server) WithDatabase(db *database.Database) *Server {
6977
s.db = db
7078
return s
@@ -156,6 +164,24 @@ func (s *Server) v1BundlesGet(w http.ResponseWriter, r *http.Request) {
156164
JSONOK(w, resp, pretty(r))
157165
}
158166

167+
func (s *Server) v1BundlesPost(w http.ResponseWriter, r *http.Request) {
168+
ctx := r.Context()
169+
170+
name, err := url.PathUnescape(r.PathValue("bundle"))
171+
if err != nil {
172+
ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err)
173+
return
174+
}
175+
176+
if err := s.svc.Trigger(ctx, s.auth(r), name); err != nil {
177+
errorAuto(w, err)
178+
return
179+
}
180+
181+
resp := types.BundlesPostResponseV1{}
182+
JSONOK(w, resp, pretty(r))
183+
}
184+
159185
func (s *Server) v1BundlesDelete(w http.ResponseWriter, r *http.Request) {
160186
ctx := r.Context()
161187

internal/server/types/types.go

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

4141
type BundlesPutResponseV1 struct{}
4242

43+
type BundlesPostResponseV1 struct{}
44+
4345
type BundlesDeleteResponseV1 struct{}
4446

4547
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/styrainc/opa-control-plane/internal/authz"
1819
"github.com/styrainc/opa-control-plane/internal/builder"
1920
"github.com/styrainc/opa-control-plane/internal/builtinsync"
2021
"github.com/styrainc/opa-control-plane/internal/config"
@@ -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)