Skip to content

Commit 3dd8f31

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 a8dc50d commit 3dd8f31

File tree

8 files changed

+104
-28
lines changed

8 files changed

+104
-28
lines changed

cmd/run/run.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package cmd
33
import (
44
"context"
55
"os"
6-
"os/signal"
7-
"syscall"
86

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

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

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:8285 --log-level debug &opactl&
2+
! stderr .
3+
! stdout .
4+
5+
exec curl --retry 5 --retry-all-errors http://127.0.0.1:8285/health
6+
7+
# automation token
8+
exec curl -H 'Authorization: bearer sesame' http://127.0.0.1:8285/v1/bundles/hello-world -XPOST
9+
10+
# admin token
11+
exec curl -H 'Authorization: bearer admin' http://127.0.0.1:8285/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
@@ -60,3 +60,9 @@ in_tenant(tenant_id) if {
6060
data.tenants.name == input.tenant
6161
tenant_id == data.tenants.id
6262
}
63+
64+
allow if {
65+
data.principals.id == input.principal
66+
data.principals.role == "automation"
67+
input.permission == "bundles.trigger"
68+
}

internal/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ func (t *Token) Equal(other *Token) bool {
801801
}
802802

803803
type Scope struct {
804-
Role string `json:"role" enum:"administrator,viewer,owner,stack_owner"`
804+
Role string `json:"role" enum:"administrator,viewer,owner,stack_owner,automation"`
805805
}
806806

807807
func scopesEqual(a, b []Scope) bool {

internal/database/database.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ func (d *Database) sourcesDataPut(ctx context.Context, sourceName, path string,
491491
Name: sourceName,
492492
})
493493
if !allowed {
494-
return errors.New("unauthorized")
494+
return ErrNotAuthorized
495495
}
496496

497497
sourceID, err := d.lookupID(ctx, tx, tenant, "sources", sourceName)
@@ -1914,6 +1914,15 @@ func (d *Database) deleteNotIn(ctx context.Context, tx *sql.Tx, table, keyColumn
19141914
return err
19151915
}
19161916

1917+
func (d *Database) Check(ctx context.Context, a authz.Access) error {
1918+
return tx1(ctx, d, func(tx *sql.Tx) error {
1919+
if !authz.Check(ctx, tx, d.arg, a) {
1920+
return ErrNotAuthorized
1921+
}
1922+
return nil
1923+
})
1924+
}
1925+
19171926
func (d *Database) arg(i int) string {
19181927
switch d.kind {
19191928
case postgres, cockroach:

internal/server/server.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ 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
const defaultTenant = "default"
2526

2627
type Server struct {
2728
router *http.ServeMux
2829
db *database.Database
30+
svc *service.Service
2931
readyFn func(context.Context) error
3032
apiPrefix string
3133
}
@@ -64,6 +66,7 @@ func (s *Server) Init() *Server {
6466
setup("GET", "/v1/bundles/{bundle}", s.v1BundlesGet)
6567
setup("PUT", "/v1/bundles/{bundle}", s.v1BundlesPut)
6668
setup("DELETE", "/v1/bundles/{bundle}", s.v1BundlesDelete)
69+
setup("POST", "/v1/bundles/{bundle}", s.v1BundlesPost)
6770

6871
setup("GET", "/v1/stacks", s.v1StacksList)
6972
setup("GET", "/v1/stacks/{stack}", s.v1StacksGet)
@@ -83,6 +86,11 @@ func (s *Server) WithRouter(router *http.ServeMux) *Server {
8386
return s
8487
}
8588

89+
func (s *Server) WithService(svc *service.Service) *Server {
90+
s.svc = svc
91+
return s
92+
}
93+
8694
func (s *Server) WithDatabase(db *database.Database) *Server {
8795
s.db = db
8896
return s
@@ -184,6 +192,24 @@ func (s *Server) v1BundlesGet(w http.ResponseWriter, r *http.Request) {
184192
JSONOK(w, resp, pretty(r))
185193
}
186194

195+
func (s *Server) v1BundlesPost(w http.ResponseWriter, r *http.Request) {
196+
ctx := r.Context()
197+
198+
name, err := url.PathUnescape(r.PathValue("bundle"))
199+
if err != nil {
200+
ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err)
201+
return
202+
}
203+
204+
if err := s.svc.Trigger(ctx, s.auth(r), name); err != nil {
205+
errorAuto(w, err)
206+
return
207+
}
208+
209+
resp := types.BundlesPostResponseV1{}
210+
JSONOK(w, resp, pretty(r))
211+
}
212+
187213
func (s *Server) v1BundlesDelete(w http.ResponseWriter, r *http.Request) {
188214
ctx := r.Context()
189215

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 & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/open-policy-agent/opa/v1/ast"
2121
_ "modernc.org/sqlite"
2222

23+
"github.com/open-policy-agent/opa-control-plane/internal/authz"
2324
"github.com/open-policy-agent/opa-control-plane/internal/builder"
2425
"github.com/open-policy-agent/opa-control-plane/internal/config"
2526
"github.com/open-policy-agent/opa-control-plane/internal/database"
@@ -37,7 +38,6 @@ import (
3738
const (
3839
internalPrincipal = "internal"
3940
defaultTenant = "default"
40-
reconfigurationInterval = 15 * time.Second
4141
defaultReconfigurationInterval = 15 * time.Second
4242
defaultBuildInterval = 30 * time.Second
4343
)
@@ -230,14 +230,17 @@ func (s *Service) Report() *Report {
230230
return s.report
231231
}
232232

233-
func (s *Service) TriggerAll(ctx context.Context) error {
234-
for name := range s.workers {
235-
return s.Trigger(ctx, name)
233+
func (s *Service) Trigger(ctx context.Context, principal, name string) error {
234+
a := authz.Access{
235+
Principal: principal,
236+
Resource: "bundles",
237+
Permission: "bundles.trigger",
238+
Name: name,
239+
}
240+
if err := s.database.Check(ctx, a); err != nil {
241+
return err
236242
}
237-
return nil
238-
}
239243

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

0 commit comments

Comments
 (0)