Skip to content

Commit b3625c0

Browse files
authored
Merge pull request #12266 from Meulengracht/release/2.57
release: port PR 12199 and 12241
2 parents c2a6891 + a3f25eb commit b3625c0

File tree

14 files changed

+299
-33
lines changed

14 files changed

+299
-33
lines changed

client/quota.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ type QuotaCPUSetValues struct {
5454
CPUs []int `json:"cpus,omitempty"`
5555
}
5656

57+
type QuotaJournalRate struct {
58+
RateCount int `json:"rate-count"`
59+
RatePeriod time.Duration `json:"rate-period"`
60+
}
61+
5762
type QuotaJournalValues struct {
58-
Size quantity.Size `json:"size,omitempty"`
59-
RateCount int `json:"rate-count,omitempty"`
60-
RatePeriod time.Duration `json:"rate-period,omitempty"`
63+
Size quantity.Size `json:"size,omitempty"`
64+
*QuotaJournalRate
6165
}
6266

6367
type QuotaValues struct {

client/quota_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ func (cs *clientSuite) TestEnsureQuotaGroup(c *check.C) {
5656
},
5757
Threads: 32,
5858
Journal: &client.QuotaJournalValues{
59-
Size: quantity.SizeMiB,
60-
RateCount: 150,
61-
RatePeriod: time.Minute,
59+
Size: quantity.SizeMiB,
60+
QuotaJournalRate: &client.QuotaJournalRate{
61+
RateCount: 150,
62+
RatePeriod: time.Minute,
63+
},
6264
},
6365
}
6466

cmd/snap/cmd_quota.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,10 @@ func (x *cmdSetQuota) parseQuotas() (*client.QuotaValues, error) {
259259
if err != nil {
260260
return nil, fmt.Errorf("cannot parse journal rate limit %q: %v", x.JournalRateLimit, err)
261261
}
262-
quotaValues.Journal.RateCount = count
263-
quotaValues.Journal.RatePeriod = period
262+
quotaValues.Journal.QuotaJournalRate = &client.QuotaJournalRate{
263+
RateCount: count,
264+
RatePeriod: period,
265+
}
264266
}
265267
}
266268

@@ -411,8 +413,10 @@ func (x *cmdQuota) Execute(args []string) (err error) {
411413
val := strings.TrimSpace(fmtSize(int64(group.Constraints.Journal.Size)))
412414
fmt.Fprintf(w, " journal-size:\t%s\n", val)
413415
}
414-
if group.Constraints.Journal.RateCount != 0 && group.Constraints.Journal.RatePeriod != 0 {
415-
fmt.Fprintf(w, " journal-rate:\t%d/%s\n", group.Constraints.Journal.RateCount, group.Constraints.Journal.RatePeriod)
416+
if group.Constraints.Journal.QuotaJournalRate != nil {
417+
fmt.Fprintf(w, " journal-rate:\t%d/%s\n",
418+
group.Constraints.Journal.RateCount,
419+
group.Constraints.Journal.RatePeriod)
416420
}
417421
}
418422

@@ -523,8 +527,11 @@ func (x *cmdQuotas) Execute(args []string) (err error) {
523527
if q.Constraints.Journal.Size != 0 {
524528
grpConstraints = append(grpConstraints, "journal-size="+strings.TrimSpace(fmtSize(int64(q.Constraints.Journal.Size))))
525529
}
526-
if q.Constraints.Journal.RateCount != 0 && q.Constraints.Journal.RatePeriod != 0 {
527-
grpConstraints = append(grpConstraints, fmt.Sprintf("journal-rate=%d/%s", q.Constraints.Journal.RateCount, q.Constraints.Journal.RatePeriod))
530+
531+
if q.Constraints.Journal.QuotaJournalRate != nil {
532+
grpConstraints = append(grpConstraints,
533+
fmt.Sprintf("journal-rate=%d/%s",
534+
q.Constraints.Journal.RateCount, q.Constraints.Journal.RatePeriod))
528535
}
529536
}
530537

cmd/snap/cmd_quota_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ func (s *quotaSuite) TestParseQuotas(c *check.C) {
223223
{journalRateLimit: "10/15s", quotas: `{"journal":{"rate-count":10,"rate-period":15000000000}}`},
224224
{journalRateLimit: "1500/15ms", quotas: `{"journal":{"rate-count":1500,"rate-period":15000000}}`},
225225
{journalRateLimit: "1/15us", quotas: `{"journal":{"rate-count":1,"rate-period":15000}}`},
226+
{journalRateLimit: "0/0s", quotas: `{"journal":{"rate-count":0,"rate-period":0}}`},
226227

227228
// Error cases
228229
{cpuMax: "ASD", err: `cannot parse cpu quota string "ASD"`},
@@ -660,11 +661,12 @@ func (s *quotaSuite) TestGetAllQuotaGroups(c *check.C) {
660661
{"group-name":"fff","parent":"aaa","constraints":{"memory":1000},"current":{"memory":0}},
661662
{"group-name":"xxx","constraints":{"memory":9900},"current":{"memory":10000}},
662663
{"group-name":"cp0","constraints":{"memory":9900, "cpu":{"percentage":90}},"current":{"memory":10000}},
663-
{"group-name":"cp1","subgroups":["cps0","js0"],"constraints":{"cpu":{"count":2, "percentage":90}}},
664+
{"group-name":"cp1","subgroups":["cps0","js0","js1"],"constraints":{"cpu":{"count":2, "percentage":90}}},
664665
{"group-name":"cps0","parent":"cp1","constraints":{"cpu":{"percentage":40}}},
665666
{"group-name":"cp2","subgroups":["cps1"],"constraints":{"cpu":{"count":2,"percentage":100},"cpu-set":{"cpus":[0,1]}}},
666667
{"group-name":"cps1","parent":"cp2","constraints":{"memory":9900,"cpu":{"percentage":50},"cpu-set":{"cpus":[1]}},"current":{"memory":10000}},
667-
{"group-name":"js0","parent":"cp1","constraints":{"journal":{"size":1048576,"rate-count":50,"rate-period":60000000000}}}
668+
{"group-name":"js0","parent":"cp1","constraints":{"journal":{"size":1048576,"rate-count":50,"rate-period":60000000000}}},
669+
{"group-name":"js1","parent":"cp1","constraints":{"journal":{"rate-count":0,"rate-period":0}}}
668670
]}`))
669671

670672
rest, err := main.Parser(main.Client()).ParseArgs([]string{"quotas"})
@@ -677,6 +679,7 @@ cp0 memory=9.9kB,cpu=90% memory=10.0kB
677679
cp1 cpu=2x90%
678680
cps0 cp1 cpu=40%
679681
js0 cp1 journal-size=1.05MB,journal-rate=50/1m0s
682+
js1 cp1 journal-rate=0/0s
680683
cp2 cpu=2x100%,cpu-set=0,1
681684
cps1 cp2 memory=9.9kB,cpu=50%,cpu-set=1 memory=10.0kB
682685
ggg memory=1000B,threads=100 memory=3000B

daemon/api_quotas.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,13 @@ func createQuotaValues(grp *quota.Group) *client.QuotaValues {
100100
}
101101
if grp.JournalLimit != nil {
102102
constraints.Journal = &client.QuotaJournalValues{
103-
Size: grp.JournalLimit.Size,
104-
RateCount: grp.JournalLimit.RateCount,
105-
RatePeriod: grp.JournalLimit.RatePeriod,
103+
Size: grp.JournalLimit.Size,
104+
}
105+
if grp.JournalLimit.RateEnabled {
106+
constraints.Journal.QuotaJournalRate = &client.QuotaJournalRate{
107+
RateCount: grp.JournalLimit.RateCount,
108+
RatePeriod: grp.JournalLimit.RatePeriod,
109+
}
106110
}
107111
}
108112
return &constraints
@@ -208,7 +212,7 @@ func quotaValuesToResources(values client.QuotaValues) quota.Resources {
208212
if values.Journal.Size != 0 {
209213
resourcesBuilder.WithJournalSize(values.Journal.Size)
210214
}
211-
if values.Journal.RateCount != 0 && values.Journal.RatePeriod != 0 {
215+
if values.Journal.QuotaJournalRate != nil {
212216
resourcesBuilder.WithJournalRate(values.Journal.RateCount, values.Journal.RatePeriod)
213217
}
214218
}

daemon/api_quotas_test.go

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ func (s *apiQuotaSuite) TestCreateQuotaValues(c *check.C) {
116116
CPUs: []int{0, 1},
117117
})
118118
c.Check(quotaValues.Journal, check.DeepEquals, &client.QuotaJournalValues{
119-
Size: quantity.SizeMiB,
120-
RateCount: 150,
121-
RatePeriod: time.Second,
119+
Size: quantity.SizeMiB,
120+
QuotaJournalRate: &client.QuotaJournalRate{
121+
RateCount: 150,
122+
RatePeriod: time.Second,
123+
},
122124
})
123125
}
124126

@@ -259,6 +261,43 @@ func (s *apiQuotaSuite) TestPostEnsureQuotaCreateQuotaConflicts(c *check.C) {
259261
c.Assert(createCalled, check.Equals, 2)
260262
}
261263

264+
func (s *apiQuotaSuite) TestPostEnsureQuotaCreateJournalRateZeroHappy(c *check.C) {
265+
var createCalled int
266+
r := daemon.MockServicestateCreateQuota(func(st *state.State, name string, parentName string, snaps []string, resourceLimits quota.Resources) (*state.TaskSet, error) {
267+
createCalled++
268+
c.Check(name, check.Equals, "booze")
269+
c.Check(parentName, check.Equals, "foo")
270+
c.Check(snaps, check.DeepEquals, []string{"some-snap"})
271+
c.Check(resourceLimits, check.DeepEquals, quota.NewResourcesBuilder().WithJournalRate(0, 0).Build())
272+
ts := state.NewTaskSet(st.NewTask("foo-quota", "..."))
273+
return ts, nil
274+
})
275+
defer r()
276+
277+
data, err := json.Marshal(daemon.PostQuotaGroupData{
278+
Action: "ensure",
279+
GroupName: "booze",
280+
Parent: "foo",
281+
Snaps: []string{"some-snap"},
282+
Constraints: client.QuotaValues{
283+
Journal: &client.QuotaJournalValues{
284+
QuotaJournalRate: &client.QuotaJournalRate{
285+
RateCount: 0,
286+
RatePeriod: 0,
287+
},
288+
},
289+
},
290+
})
291+
c.Assert(err, check.IsNil)
292+
293+
req, err := http.NewRequest("POST", "/v2/quotas", bytes.NewBuffer(data))
294+
c.Assert(err, check.IsNil)
295+
rsp := s.asyncReq(c, req, nil)
296+
c.Assert(rsp.Status, check.Equals, 202)
297+
c.Assert(createCalled, check.Equals, 1)
298+
c.Assert(s.ensureSoonCalled, check.Equals, 1)
299+
}
300+
262301
func (s *apiQuotaSuite) TestPostEnsureQuotaUpdateCpuHappy(c *check.C) {
263302
st := s.d.Overlord().State()
264303
st.Lock()
@@ -677,6 +716,68 @@ func (s *apiQuotaSuite) TestListQuotas(c *check.C) {
677716
c.Check(s.ensureSoonCalled, check.Equals, 0)
678717
}
679718

719+
func (s *apiQuotaSuite) TestListJournalQuotas(c *check.C) {
720+
st := s.d.Overlord().State()
721+
st.Lock()
722+
err := servicestatetest.MockQuotaInState(st, "foo", "", nil, quota.NewResourcesBuilder().WithJournalSize(64*quantity.SizeMiB).Build())
723+
c.Assert(err, check.IsNil)
724+
err = servicestatetest.MockQuotaInState(st, "bar", "foo", nil, quota.NewResourcesBuilder().WithJournalRate(100, time.Hour).Build())
725+
c.Assert(err, check.IsNil)
726+
err = servicestatetest.MockQuotaInState(st, "baz", "foo", nil, quota.NewResourcesBuilder().WithJournalRate(0, 0).Build())
727+
c.Assert(err, check.IsNil)
728+
st.Unlock()
729+
730+
calls := 0
731+
r := daemon.MockGetQuotaUsage(func(grp *quota.Group) (*client.QuotaValues, error) {
732+
calls++
733+
return &client.QuotaValues{}, nil
734+
})
735+
defer r()
736+
defer func() {
737+
c.Assert(calls, check.Equals, 3)
738+
}()
739+
740+
req, err := http.NewRequest("GET", "/v2/quotas", nil)
741+
c.Assert(err, check.IsNil)
742+
rsp := s.syncReq(c, req, nil)
743+
c.Assert(rsp.Status, check.Equals, 200)
744+
c.Assert(rsp.Result, check.FitsTypeOf, []client.QuotaGroupResult{})
745+
res := rsp.Result.([]client.QuotaGroupResult)
746+
c.Check(res, check.DeepEquals, []client.QuotaGroupResult{
747+
{
748+
GroupName: "bar",
749+
Parent: "foo",
750+
Constraints: &client.QuotaValues{Journal: &client.QuotaJournalValues{
751+
QuotaJournalRate: &client.QuotaJournalRate{
752+
RateCount: 100,
753+
RatePeriod: time.Hour,
754+
},
755+
}},
756+
Current: &client.QuotaValues{},
757+
},
758+
{
759+
GroupName: "baz",
760+
Parent: "foo",
761+
Constraints: &client.QuotaValues{Journal: &client.QuotaJournalValues{
762+
QuotaJournalRate: &client.QuotaJournalRate{
763+
RateCount: 0,
764+
RatePeriod: 0,
765+
},
766+
}},
767+
Current: &client.QuotaValues{},
768+
},
769+
{
770+
GroupName: "foo",
771+
Subgroups: []string{"bar", "baz"},
772+
Constraints: &client.QuotaValues{Journal: &client.QuotaJournalValues{
773+
Size: 64 * quantity.SizeMiB,
774+
}},
775+
Current: &client.QuotaValues{},
776+
},
777+
})
778+
c.Check(s.ensureSoonCalled, check.Equals, 0)
779+
}
780+
680781
func (s *apiQuotaSuite) TestGetQuota(c *check.C) {
681782
st := s.d.Overlord().State()
682783
st.Lock()

overlord/devicestate/handlers_gadget.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ func (m *DeviceManager) updateGadgetCommandLine(t *state.Task, st *state.State,
232232
if !isUndo {
233233
// when updating, command line comes from the new gadget
234234
gadgetData, err = pendingGadgetInfo(snapsup, devCtx)
235+
if err != nil {
236+
return false, err
237+
}
235238
} else {
236239
// but when undoing, we use the current gadget which should have
237240
// been restored

overlord/servicestate/quota_handlers.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -574,10 +574,16 @@ func ensureSnapServicesForGroup(st *state.State, t *state.Task, grp *quota.Group
574574
}
575575

576576
case "service":
577-
// in this case, the only way that a service could have been changed
578-
// was if it was moved into or out of a slice, in both cases we need
579-
// to restart the service
580-
markAppForRestart(app.Snap, app)
577+
// When an app has its service file changed, we should restart the app
578+
// to take into account that the app might have been moved in/out of a
579+
// slice, which means limits may have changed.
580+
if app != nil {
581+
markAppForRestart(app.Snap, app)
582+
}
583+
584+
// If a quota group has it's service file changed, then it's due to the
585+
// journal quota being set. We do not need to do any further changes here
586+
// as restart of apps in the journal quota is being handled by case 'journald'
581587

582588
// TODO: what about sockets and timers? activation units just start
583589
// the full unit, so as long as the full unit is restarted we should

overlord/snapstate/handlers.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2358,7 +2358,11 @@ func installModeDisabledServices(st *state.State, snapst *SnapState, currentInfo
23582358
prevCurrentSvcs := map[string]bool{}
23592359
if psi := snapst.previousSideInfo(); psi != nil {
23602360
var prevCurrentInfo *snap.Info
2361-
if prevCurrentInfo, err = Info(st, snapst.InstanceName(), psi.Revision); prevCurrentInfo != nil {
2361+
prevCurrentInfo, err = Info(st, snapst.InstanceName(), psi.Revision)
2362+
if err != nil {
2363+
return nil, err
2364+
}
2365+
if prevCurrentInfo != nil {
23622366
for _, prevSvc := range prevCurrentInfo.Services() {
23632367
prevCurrentSvcs[prevSvc.Name] = true
23642368
}

snap/quota/quota.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ package quota
2424
import (
2525
"bytes"
2626
"fmt"
27+
"path/filepath"
2728
"runtime"
2829
"sort"
2930
"time"
3031

3132
// TODO: move this to snap/quantity? or similar
33+
"github.com/snapcore/snapd/dirs"
3234
"github.com/snapcore/snapd/gadget/quantity"
3335
"github.com/snapcore/snapd/progress"
3436
"github.com/snapcore/snapd/snap/naming"
@@ -266,13 +268,30 @@ func (grp *Group) JournalNamespaceName() string {
266268
return fmt.Sprintf("snap-%s", grp.Name)
267269
}
268270

269-
// JournalFileName returns the name of the journal configuration file that should
271+
// JournalConfFileName returns the name of the journal configuration file that should
270272
// be used for this quota group. As an example, a group named "foo" will return a name
271273
272-
func (grp *Group) JournalFileName() string {
274+
func (grp *Group) JournalConfFileName() string {
273275
return fmt.Sprintf("journald@%s.conf", grp.JournalNamespaceName())
274276
}
275277

278+
// JournalServiceName returns the systemd service name for the quota group.
279+
func (grp *Group) JournalServiceName() string {
280+
return fmt.Sprintf("systemd-journald@%s.service", grp.JournalNamespaceName())
281+
}
282+
283+
// JournalServiceFile returns the directory specific to this quota group for
284+
// its journal service unit drop-in.
285+
func (grp *Group) JournalServiceDropInDir() string {
286+
return filepath.Join(dirs.SnapServicesDir, grp.JournalServiceName()+".d")
287+
}
288+
289+
// JournalServiceDropInFile returns the full path to the journal service unit drop-in
290+
// file for the quota group.
291+
func (grp *Group) JournalServiceDropInFile() string {
292+
return filepath.Join(grp.JournalServiceDropInDir(), "00-snap.conf")
293+
}
294+
276295
// groupQuotaAllocations contains information about current quotas of a group
277296
// and is used by getQuotaAllocations to contain this information. This only accounts
278297
// for quotas that support inheritance, which currently does not include journal quotas.

0 commit comments

Comments
 (0)