Skip to content

Commit

Permalink
Support daemon API changes from sister projects (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-rodriguez committed Aug 4, 2023
1 parent f593810 commit 73aa51e
Show file tree
Hide file tree
Showing 15 changed files with 68 additions and 38 deletions.
4 changes: 2 additions & 2 deletions internals/daemon/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"github.com/canonical/pebble/internals/overlord/state"
)

var api = []*Command{{
var API = []*Command{{
// See daemon.go:canAccess for details how the access is controlled.
Path: "/v1/system-info",
GuestOK: true,
Expand Down Expand Up @@ -107,7 +107,7 @@ var (
muxVars = mux.Vars
)

func v1SystemInfo(c *Command, r *http.Request, _ *userState) Response {
func v1SystemInfo(c *Command, r *http.Request, _ *UserState) Response {
state := c.d.overlord.State()
state.Lock()
defer state.Unlock()
Expand Down
8 changes: 4 additions & 4 deletions internals/daemon/api_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func change2changeInfo(chg *state.Change) *changeInfo {
return chgInfo
}

func v1GetChanges(c *Command, r *http.Request, _ *userState) Response {
func v1GetChanges(c *Command, r *http.Request, _ *UserState) Response {
query := r.URL.Query()
qselect := query.Get("select")
if qselect == "" {
Expand Down Expand Up @@ -170,7 +170,7 @@ func v1GetChanges(c *Command, r *http.Request, _ *userState) Response {
return SyncResponse(chgInfos)
}

func v1GetChange(c *Command, r *http.Request, _ *userState) Response {
func v1GetChange(c *Command, r *http.Request, _ *UserState) Response {
changeID := muxVars(r)["id"]
st := c.d.overlord.State()
st.Lock()
Expand All @@ -183,7 +183,7 @@ func v1GetChange(c *Command, r *http.Request, _ *userState) Response {
return SyncResponse(change2changeInfo(chg))
}

func v1GetChangeWait(c *Command, r *http.Request, _ *userState) Response {
func v1GetChangeWait(c *Command, r *http.Request, _ *UserState) Response {
changeID := muxVars(r)["id"]
st := c.d.overlord.State()
st.Lock()
Expand Down Expand Up @@ -224,7 +224,7 @@ func v1GetChangeWait(c *Command, r *http.Request, _ *userState) Response {
return SyncResponse(change2changeInfo(change))
}

func v1PostChange(c *Command, r *http.Request, _ *userState) Response {
func v1PostChange(c *Command, r *http.Request, _ *UserState) Response {
chID := muxVars(r)["id"]
state := c.d.overlord.State()
state.Lock()
Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type checkInfo struct {
Threshold int `json:"threshold"`
}

func v1GetChecks(c *Command, r *http.Request, _ *userState) Response {
func v1GetChecks(c *Command, r *http.Request, _ *UserState) Response {
query := r.URL.Query()
level := plan.CheckLevel(query.Get("level"))
switch level {
Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type execPayload struct {
Height int `json:"height"`
}

func v1PostExec(c *Command, req *http.Request, _ *userState) Response {
func v1PostExec(c *Command, req *http.Request, _ *UserState) Response {
var payload execPayload
decoder := json.NewDecoder(req.Body)
if err := decoder.Decode(&payload); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internals/daemon/api_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (

const minBoundaryLength = 32

func v1GetFiles(_ *Command, req *http.Request, _ *userState) Response {
func v1GetFiles(_ *Command, req *http.Request, _ *UserState) Response {
query := req.URL.Query()
action := query.Get("action")
switch action {
Expand Down Expand Up @@ -337,7 +337,7 @@ func listFiles(path, pattern string, itself bool) ([]fileInfoResult, error) {
return result, nil
}

func v1PostFiles(_ *Command, req *http.Request, _ *userState) Response {
func v1PostFiles(_ *Command, req *http.Request, _ *UserState) Response {
contentType := req.Header.Get("Content-Type")
mediaType, params, err := mime.ParseMediaType(contentType)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_health.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type healthInfo struct {
Healthy bool `json:"healthy"`
}

func v1Health(c *Command, r *http.Request, _ *userState) Response {
func v1Health(c *Command, r *http.Request, _ *UserState) Response {
query := r.URL.Query()
level := plan.CheckLevel(query.Get("level"))
switch level {
Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type serviceManager interface {
ServiceLogs(services []string, last int) (map[string]servicelog.Iterator, error)
}

func v1GetLogs(c *Command, _ *http.Request, _ *userState) Response {
func v1GetLogs(c *Command, _ *http.Request, _ *UserState) Response {
return logsResponse{
svcMgr: overlordServiceManager(c.d.overlord),
}
Expand Down
4 changes: 2 additions & 2 deletions internals/daemon/api_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"github.com/canonical/pebble/internals/plan"
)

func v1GetPlan(c *Command, r *http.Request, _ *userState) Response {
func v1GetPlan(c *Command, r *http.Request, _ *UserState) Response {
format := r.URL.Query().Get("format")
if format != "yaml" {
return statusBadRequest("invalid format %q", format)
Expand All @@ -42,7 +42,7 @@ func v1GetPlan(c *Command, r *http.Request, _ *userState) Response {
return SyncResponse(string(planYAML))
}

func v1PostLayers(c *Command, r *http.Request, _ *userState) Response {
func v1PostLayers(c *Command, r *http.Request, _ *UserState) Response {
var payload struct {
Action string `json:"action"`
Combine bool `json:"combine"`
Expand Down
8 changes: 4 additions & 4 deletions internals/daemon/api_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type serviceInfo struct {
CurrentSince *time.Time `json:"current-since,omitempty"` // pointer as omitempty doesn't work with time.Time directly
}

func v1GetServices(c *Command, r *http.Request, _ *userState) Response {
func v1GetServices(c *Command, r *http.Request, _ *UserState) Response {
names := strutil.MultiCommaSeparatedList(r.URL.Query()["names"])

servmgr := overlordServiceManager(c.d.overlord)
Expand All @@ -59,7 +59,7 @@ func v1GetServices(c *Command, r *http.Request, _ *userState) Response {
return SyncResponse(infos)
}

func v1PostServices(c *Command, r *http.Request, _ *userState) Response {
func v1PostServices(c *Command, r *http.Request, _ *UserState) Response {
var payload struct {
Action string `json:"action"`
Services []string `json:"services"`
Expand Down Expand Up @@ -212,11 +212,11 @@ func v1PostServices(c *Command, r *http.Request, _ *userState) Response {
return AsyncResponse(nil, change.ID())
}

func v1GetService(c *Command, r *http.Request, _ *userState) Response {
func v1GetService(c *Command, r *http.Request, _ *UserState) Response {
return statusBadRequest("not implemented")
}

func v1PostService(c *Command, r *http.Request, _ *userState) Response {
func v1PostService(c *Command, r *http.Request, _ *UserState) Response {
return statusBadRequest("not implemented")
}

Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_signals.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type signalsPayload struct {
Services []string `json:"services"`
}

func v1PostSignals(c *Command, req *http.Request, _ *userState) Response {
func v1PostSignals(c *Command, req *http.Request, _ *UserState) Response {
var payload signalsPayload
decoder := json.NewDecoder(req.Body)
if err := decoder.Decode(&payload); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/canonical/pebble/internals/overlord/state"
)

func v1GetTaskWebsocket(c *Command, req *http.Request, _ *userState) Response {
func v1GetTaskWebsocket(c *Command, req *http.Request, _ *UserState) Response {
vars := muxVars(req)
taskID := vars["task-id"]
websocketID := vars["websocket-id"]
Expand Down
2 changes: 1 addition & 1 deletion internals/daemon/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *apiSuite) daemon(c *check.C) *Daemon {
}

func apiCmd(path string) *Command {
for _, cmd := range api {
for _, cmd := range API {
if cmd.Path == path {
return cmd
}
Expand Down
4 changes: 2 additions & 2 deletions internals/daemon/api_warnings.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/canonical/pebble/internals/overlord/state"
)

func v1AckWarnings(c *Command, r *http.Request, _ *userState) Response {
func v1AckWarnings(c *Command, r *http.Request, _ *UserState) Response {
defer r.Body.Close()
var op struct {
Action string `json:"action"`
Expand All @@ -43,7 +43,7 @@ func v1AckWarnings(c *Command, r *http.Request, _ *userState) Response {
return SyncResponse(n)
}

func v1GetWarnings(c *Command, r *http.Request, _ *userState) Response {
func v1GetWarnings(c *Command, r *http.Request, _ *UserState) Response {
query := r.URL.Query()
var all bool
sel := query.Get("select")
Expand Down
15 changes: 9 additions & 6 deletions internals/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,14 @@ type Daemon struct {
mu sync.Mutex
}

// XXX Placeholder for now.
type userState struct{}
// UserState represents the state of an authenticated API user.
//
// The struct is currently empty as the behaviors haven't been implemented
// yet.
type UserState struct{}

// A ResponseFunc handles one of the individual verbs for a method
type ResponseFunc func(*Command, *http.Request, *userState) Response
type ResponseFunc func(*Command, *http.Request, *UserState) Response

// A Command routes a request to an individual per-verb ResponseFUnc
type Command struct {
Expand Down Expand Up @@ -149,7 +152,7 @@ const (
// - UserOK: any uid on the local system can access GET
// - AdminOnly: only the administrator can access this
// - UntrustedOK: can access this via the untrusted socket
func (c *Command) canAccess(r *http.Request, user *userState) accessResult {
func (c *Command) canAccess(r *http.Request, user *UserState) accessResult {
if c.AdminOnly && (c.UserOK || c.GuestOK || c.UntrustedOK) {
logger.Panicf("internal error: command cannot have AdminOnly together with any *OK flag")
}
Expand Down Expand Up @@ -210,7 +213,7 @@ func (c *Command) canAccess(r *http.Request, user *userState) accessResult {
return accessUnauthorized
}

func userFromRequest(state interface{}, r *http.Request) (*userState, error) {
func userFromRequest(state interface{}, r *http.Request) (*UserState, error) {
return nil, nil
}

Expand Down Expand Up @@ -401,7 +404,7 @@ func (d *Daemon) SetDegradedMode(err error) {
func (d *Daemon) addRoutes() {
d.router = mux.NewRouter()

for _, c := range api {
for _, c := range API {
c.d = d
if c.PathPrefix == "" {
d.router.Handle(c.Path, c).Name(c.Path)
Expand Down
45 changes: 36 additions & 9 deletions internals/daemon/daemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,33 @@ func (h *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.lastMethod = r.Method
}

func (s *daemonSuite) TestAddCommand(c *C) {
const endpoint = "/v1/addedendpoint"
var handler fakeHandler
getCallback := func(c *Command, r *http.Request, s *UserState) Response {
handler.cmd = c
return &handler
}
command := Command{
Path: endpoint,
GuestOK: true,
GET: getCallback,
}
API = append(API, &command)
defer func() {
c.Assert(API[len(API)-1], Equals, &command)
API = API[:len(API)-1]
}()

d := s.newDaemon(c)
d.Init()
d.Start()
defer d.Stop(nil)

result := d.router.Get(endpoint).GetHandler()
c.Assert(result, Equals, &command)
}

func (s *daemonSuite) TestExplicitPaths(c *C) {
s.socketPath = filepath.Join(c.MkDir(), "custom.socket")

Expand All @@ -121,7 +148,7 @@ func (s *daemonSuite) TestCommandMethodDispatch(c *C) {

cmd := &Command{d: s.newDaemon(c)}
handler := &fakeHandler{cmd: cmd}
rf := func(innerCmd *Command, req *http.Request, user *userState) Response {
rf := func(innerCmd *Command, req *http.Request, user *UserState) Response {
c.Assert(cmd, Equals, innerCmd)
return handler
}
Expand Down Expand Up @@ -160,7 +187,7 @@ func (s *daemonSuite) TestCommandRestartingState(c *C) {
d := s.newDaemon(c)

cmd := &Command{d: d}
cmd.GET = func(*Command, *http.Request, *userState) Response {
cmd.GET = func(*Command, *http.Request, *UserState) Response {
return SyncResponse(nil)
}
req, err := http.NewRequest("GET", "", nil)
Expand Down Expand Up @@ -210,7 +237,7 @@ func (s *daemonSuite) TestFillsWarnings(c *C) {
d := s.newDaemon(c)

cmd := &Command{d: d}
cmd.GET = func(*Command, *http.Request, *userState) Response {
cmd.GET = func(*Command, *http.Request, *UserState) Response {
return SyncResponse(nil)
}
req, err := http.NewRequest("GET", "", nil)
Expand Down Expand Up @@ -341,7 +368,7 @@ func (s *daemonSuite) TestUserAccess(c *C) {
func (s *daemonSuite) TestLoggedInUserAccess(c *C) {
d := s.newDaemon(c)

user := &userState{}
user := &UserState{}
get := &http.Request{Method: "GET", RemoteAddr: "pid=100;uid=42;socket=;"}
put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=42;socket=;"}

Expand Down Expand Up @@ -399,16 +426,16 @@ func (s *daemonSuite) TestSuperAccess(c *C) {
func (s *daemonSuite) TestAddRoutes(c *C) {
d := s.newDaemon(c)

expected := make([]string, len(api))
for i, v := range api {
expected := make([]string, len(API))
for i, v := range API {
if v.PathPrefix != "" {
expected[i] = v.PathPrefix
continue
}
expected[i] = v.Path
}

got := make([]string, 0, len(api))
got := make([]string, 0, len(API))
c.Assert(d.router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
got = append(got, route.GetName())
return nil
Expand Down Expand Up @@ -1028,10 +1055,10 @@ func doTestReq(c *C, cmd *Command, mth string) *httptest.ResponseRecorder {
func (s *daemonSuite) TestDegradedModeReply(c *C) {
d := s.newDaemon(c)
cmd := &Command{d: d}
cmd.GET = func(*Command, *http.Request, *userState) Response {
cmd.GET = func(*Command, *http.Request, *UserState) Response {
return SyncResponse(nil)
}
cmd.POST = func(*Command, *http.Request, *userState) Response {
cmd.POST = func(*Command, *http.Request, *UserState) Response {
return SyncResponse(nil)
}

Expand Down

0 comments on commit 73aa51e

Please sign in to comment.