Skip to content

Commit

Permalink
Merge pull request #384 from kjaskiewiczz/alv-116
Browse files Browse the repository at this point in the history
feat: introduce plans API
  • Loading branch information
kjaskiewiczz authored Oct 18, 2023
2 parents 4ad629c + e1595bc commit 7801cd3
Show file tree
Hide file tree
Showing 12 changed files with 833 additions and 18 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ USER 65534
WORKDIR /etc/useradm/rsa
COPY --from=builder --chown=nobody /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --chown=nobody ./config.yaml /etc/useradm/
COPY --chown=nobody ./config/plans.yaml /etc/useradm/
COPY --from=builder --chown=nobody /go/src/github.com/mendersoftware/useradm/useradm /usr/bin/

ENTRYPOINT ["/usr/bin/useradm", "--config", "/etc/useradm/config.yaml"]
1 change: 1 addition & 0 deletions Dockerfile.acceptance-testing
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ COPY --from=builder /etc_extra/ /etc/
USER 65534
WORKDIR /etc/useradm/rsa
COPY --chown=nobody ./config.yaml /etc/useradm/
COPY --chown=nobody ./config/plans.yaml /etc/useradm/
COPY --from=builder --chown=nobody /go/src/github.com/mendersoftware/useradm/useradm /usr/bin/
COPY --from=builder --chown=nobody /tmp/gocovedir /tmp/gocovedir
ENV GOCOVERDIR=/tmp/gocovedir
Expand Down
55 changes: 46 additions & 9 deletions api/http/api_useradm.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ import (
)

const (
apiUrlManagementV1 = "/api/management/v1/useradm"
uriManagementAuthLogin = apiUrlManagementV1 + "/auth/login"
uriManagementAuthLogout = apiUrlManagementV1 + "/auth/logout"
uriManagementUser = apiUrlManagementV1 + "/users/#id"
uriManagementUsers = apiUrlManagementV1 + "/users"
uriManagementSettings = apiUrlManagementV1 + "/settings"
uriManagementSettingsMe = apiUrlManagementV1 + "/settings/me"
uriManagementTokens = apiUrlManagementV1 + "/settings/tokens"
uriManagementToken = apiUrlManagementV1 + "/settings/tokens/#id"
apiUrlManagementV1 = "/api/management/v1/useradm"
uriManagementAuthLogin = apiUrlManagementV1 + "/auth/login"
uriManagementAuthLogout = apiUrlManagementV1 + "/auth/logout"
uriManagementUser = apiUrlManagementV1 + "/users/#id"
uriManagementUsers = apiUrlManagementV1 + "/users"
uriManagementSettings = apiUrlManagementV1 + "/settings"
uriManagementSettingsMe = apiUrlManagementV1 + "/settings/me"
uriManagementTokens = apiUrlManagementV1 + "/settings/tokens"
uriManagementToken = apiUrlManagementV1 + "/settings/tokens/#id"
uriManagementPlans = apiUrlManagementV1 + "/plans"
uriManagementPlanBinding = apiUrlManagementV1 + "/plan_binding"

apiUrlInternalV1 = "/api/internal/v1/useradm"
uriInternalAlive = apiUrlInternalV1 + "/alive"
Expand Down Expand Up @@ -125,6 +127,9 @@ func (i *UserAdmApiHandlers) GetApp() (rest.App, error) {
rest.Post(uriManagementTokens, i.IssueTokenHandler),
rest.Get(uriManagementTokens, i.GetTokensHandler),
rest.Delete(uriManagementToken, i.DeleteTokenHandler),
// plans
rest.Get(uriManagementPlans, i.GetPlansHandler),
rest.Get(uriManagementPlanBinding, i.GetPlanBindingHandler),
}

app, err := rest.MakeRouter(
Expand Down Expand Up @@ -748,3 +753,35 @@ func (u *UserAdmApiHandlers) DeleteTokenHandler(w rest.ResponseWriter, r *rest.R

w.WriteHeader(http.StatusNoContent)
}

// plans and plan binding

func (u *UserAdmApiHandlers) GetPlansHandler(w rest.ResponseWriter, r *rest.Request) {
ctx := r.Context()
l := log.FromContext(ctx)
page, perPage, err := rest_utils.ParsePagination(r)
if err != nil {
rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
return
}

plans := u.userAdm.GetPlans(ctx, int((page-1)*perPage), int(perPage))
if plans == nil {
plans = []model.Plan{}
}

_ = w.WriteJson(plans)
}

func (u *UserAdmApiHandlers) GetPlanBindingHandler(w rest.ResponseWriter, r *rest.Request) {
ctx := r.Context()
l := log.FromContext(ctx)

planBinding, err := u.userAdm.GetPlanBinding(ctx)
if err != nil {
rest_utils.RestErrWithLogInternal(w, r, l, err)
return
}

_ = w.WriteJson(planBinding)
}
184 changes: 184 additions & 0 deletions api/http/api_useradm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2114,3 +2114,187 @@ func TestUserAdmApiGetTokens(t *testing.T) {
})
}
}

func TestUserAdmApiGetPlans(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
uaSkip int
uaLimit int
uaPlans []model.Plan
uaError error
uaCallGetPlans bool
page string
perPage string

checker mt.ResponseChecker
}{
"ok": {
uaSkip: 0,
uaLimit: 20,
uaPlans: []model.Plan{
{
Name: "foo",
},
},
uaError: nil,
uaCallGetPlans: true,

checker: mt.NewJSONResponse(
http.StatusOK,
nil,
[]model.Plan{
{
Name: "foo",
},
},
),
},
"ok, no plans": {
uaSkip: 0,
uaLimit: 20,
uaPlans: nil,
uaCallGetPlans: true,

checker: mt.NewJSONResponse(
http.StatusOK,
nil,
[]model.Plan{},
),
},
"ok, with pagination": {
page: "10",
perPage: "10",
uaSkip: 90,
uaLimit: 10,
uaPlans: []model.Plan{
{
Name: "foo",
},
},
uaError: nil,
uaCallGetPlans: true,

checker: mt.NewJSONResponse(
http.StatusOK,
nil,
[]model.Plan{
{
Name: "foo",
},
},
),
},
"error: wrong page": {
page: "foo",

checker: mt.NewJSONResponse(
http.StatusBadRequest,
nil,
restError("Can't parse param page"),
),
},
"error: wrong per_page": {
perPage: "foo",

checker: mt.NewJSONResponse(
http.StatusBadRequest,
nil,
restError("Can't parse param per_page"),
),
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
ctx := identity.WithContext(context.Background(), &identity.Identity{Subject: "123"})

//make mock useradm
uadm := &museradm.App{}
defer uadm.AssertExpectations(t)

if tc.uaCallGetPlans {
uadm.On("GetPlans", mtesting.ContextMatcher(), tc.uaSkip, tc.uaLimit).
Return(tc.uaPlans, tc.uaError)
}

//make handler
api := makeMockApiHandler(t, uadm, nil)

//make request
req := makeReq("GET",
"http://1.2.3.4"+uriManagementPlans+"?page="+tc.page+"&per_page="+tc.perPage,
"",
nil)

//test
recorded := test.RunRequest(t, api, req.WithContext(ctx))
mt.CheckResponse(t, tc.checker, recorded)
})
}
}

func TestUserAdmApiGetPlanBinding(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
uaPlanBinding *model.PlanBindingDetails
uaError error

checker mt.ResponseChecker
}{
"ok": {
uaPlanBinding: &model.PlanBindingDetails{
Plan: model.Plan{
Name: "foo",
},
},
uaError: nil,

checker: mt.NewJSONResponse(
http.StatusOK,
nil,
&model.PlanBindingDetails{
Plan: model.Plan{
Name: "foo",
},
},
),
},
"error": {
uaError: errors.New("foo"),

checker: mt.NewJSONResponse(
http.StatusInternalServerError,
nil,
restError("internal error"),
),
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
ctx := identity.WithContext(context.Background(), &identity.Identity{Subject: "123"})

//make mock useradm
uadm := &museradm.App{}
defer uadm.AssertExpectations(t)

uadm.On("GetPlanBinding", mtesting.ContextMatcher()).
Return(tc.uaPlanBinding, tc.uaError)

//make handler
api := makeMockApiHandler(t, uadm, nil)

//make request
req := makeReq("GET",
"http://1.2.3.4"+uriManagementPlanBinding,
"",
nil)

//test
recorded := test.RunRequest(t, api, req.WithContext(ctx))
mt.CheckResponse(t, tc.checker, recorded)
})
}
}
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const (

SettingTokenMaxExpirationSeconds = "token_max_expiration_seconds"
SettingTokenMaxExpirationSecondsDefault = 31536000

SettingPlanDefinitions = "plan_definitions_path"
SettingPlanDefinitionsDefault = "/etc/useradm/plans.yaml"
)

var (
Expand All @@ -83,5 +86,7 @@ var (
Value: SettingTokenLastUsedUpdateFreqMinutesDefault},
{Key: SettingTokenMaxExpirationSeconds,
Value: SettingTokenMaxExpirationSecondsDefault},
{Key: SettingPlanDefinitions,
Value: SettingPlanDefinitionsDefault},
}
)
10 changes: 10 additions & 0 deletions config/plans.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plans:
Mender_Open_Source:
display_name: Mender
features:
rbac: false
audit_logs: false
dynamic_groups: false
terminal: false
configuration: true
monitoring: false
Loading

0 comments on commit 7801cd3

Please sign in to comment.