gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/servicestate/internal/quotas_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package internal_test
    21  
    22  import (
    23  	"testing"
    24  
    25  	. "gopkg.in/check.v1"
    26  
    27  	"github.com/snapcore/snapd/gadget/quantity"
    28  	"github.com/snapcore/snapd/overlord/servicestate/internal"
    29  	"github.com/snapcore/snapd/overlord/state"
    30  	"github.com/snapcore/snapd/snap/quota"
    31  )
    32  
    33  func TestInternal(t *testing.T) { TestingT(t) }
    34  
    35  type servicestateQuotasSuite struct {
    36  	state *state.State
    37  }
    38  
    39  var _ = Suite(&servicestateQuotasSuite{})
    40  
    41  func (s *servicestateQuotasSuite) SetUpTest(c *C) {
    42  	s.state = state.New(nil)
    43  }
    44  
    45  func (s *servicestateQuotasSuite) TestQuotas(c *C) {
    46  	st := s.state
    47  	st.Lock()
    48  	defer st.Unlock()
    49  
    50  	// with nothing in state we don't get any quotas
    51  	quotaMap, err := internal.AllQuotas(st)
    52  	c.Assert(err, IsNil)
    53  	c.Assert(quotaMap, HasLen, 0)
    54  
    55  	// we can add some basic quotas to state
    56  	grp := &quota.Group{
    57  		Name:        "foogroup",
    58  		MemoryLimit: quantity.SizeGiB,
    59  	}
    60  	newGrps, err := internal.PatchQuotas(st, grp)
    61  	c.Assert(err, IsNil)
    62  	c.Assert(newGrps, DeepEquals, map[string]*quota.Group{
    63  		"foogroup": grp,
    64  	})
    65  
    66  	// now we get back the same quota
    67  	quotaMap, err = internal.AllQuotas(st)
    68  	c.Assert(err, IsNil)
    69  	c.Assert(quotaMap, DeepEquals, map[string]*quota.Group{
    70  		"foogroup": grp,
    71  	})
    72  
    73  	// adding a sub-group quota only works when we update the parent group to
    74  	// reference the sub-group at the same time
    75  	grp2 := &quota.Group{
    76  		Name:        "group-2",
    77  		MemoryLimit: quantity.SizeGiB,
    78  		ParentGroup: "foogroup",
    79  	}
    80  	_, err = internal.PatchQuotas(st, grp2)
    81  	c.Assert(err, ErrorMatches, `cannot update quota "group-2": group "foogroup" does not reference necessary child group "group-2"`)
    82  
    83  	// we also can't add a sub-group to the parent without adding the sub-group
    84  	// itself
    85  	grp.SubGroups = append(grp.SubGroups, "group-2")
    86  	_, err = internal.PatchQuotas(st, grp)
    87  	c.Assert(err, ErrorMatches, `cannot update quota "foogroup": missing group "group-2" referenced as the sub-group of group "foogroup"`)
    88  
    89  	// foogroup didn't get updated in the state to mention the sub-group
    90  	quotaMap, err = internal.AllQuotas(st)
    91  	c.Assert(err, IsNil)
    92  	foogrp, ok := quotaMap["foogroup"]
    93  	c.Assert(ok, Equals, true)
    94  	c.Assert(foogrp.SubGroups, HasLen, 0)
    95  
    96  	// but if we update them both at the same time we succeed
    97  	newGrps, err = internal.PatchQuotas(st, grp, grp2)
    98  	c.Assert(err, IsNil)
    99  	c.Assert(newGrps, DeepEquals, map[string]*quota.Group{
   100  		"foogroup": grp,
   101  		"group-2":  grp2,
   102  	})
   103  
   104  	// and now we see both in the state
   105  	quotaMap, err = internal.AllQuotas(st)
   106  	c.Assert(err, IsNil)
   107  	c.Assert(quotaMap, DeepEquals, map[string]*quota.Group{
   108  		"foogroup": grp,
   109  		"group-2":  grp2,
   110  	})
   111  
   112  	// adding multiple quotas that are invalid produces a nice error message
   113  	otherGrp := &quota.Group{
   114  		Name: "other-group",
   115  		// invalid memory limit
   116  	}
   117  
   118  	otherGrp2 := &quota.Group{
   119  		Name: "other-group2",
   120  		// invalid memory limit
   121  	}
   122  
   123  	_, err = internal.PatchQuotas(st, otherGrp2, otherGrp)
   124  	// either group can get checked first
   125  	c.Assert(err, ErrorMatches, `cannot update quotas "other-group", "other-group2": group "other-group2?" is invalid: group memory limit must be non-zero`)
   126  }
   127  
   128  func (s *servicestateQuotasSuite) TestCreateQuotaInState(c *C) {
   129  	st := s.state
   130  	st.Lock()
   131  	defer st.Unlock()
   132  
   133  	// we can create a basic quota in state
   134  	grp := &quota.Group{
   135  		Name:        "foogroup",
   136  		MemoryLimit: quantity.SizeGiB,
   137  	}
   138  	grp1, newGrps, err := internal.CreateQuotaInState(st, "foogroup", nil, nil, quantity.SizeGiB, nil)
   139  	c.Assert(err, IsNil)
   140  	c.Check(grp1, DeepEquals, grp)
   141  	c.Check(newGrps, DeepEquals, map[string]*quota.Group{
   142  		"foogroup": grp,
   143  	})
   144  
   145  	// now quota is really in state
   146  	quotaMap, err := internal.AllQuotas(st)
   147  	c.Assert(err, IsNil)
   148  	c.Assert(quotaMap, DeepEquals, map[string]*quota.Group{
   149  		"foogroup": grp,
   150  	})
   151  
   152  	// create a sub-group quota
   153  	grp2 := &quota.Group{
   154  		Name:        "group-2",
   155  		MemoryLimit: quantity.SizeGiB,
   156  		ParentGroup: "foogroup",
   157  		Snaps:       []string{"snap1", "snap2"},
   158  	}
   159  	grp3, newGrps, err := internal.CreateQuotaInState(st, "group-2", grp1, []string{"snap1", "snap2"}, quantity.SizeGiB, nil)
   160  	c.Assert(err, IsNil)
   161  	c.Check(grp3.Name, Equals, grp2.Name)
   162  	c.Check(grp3.MemoryLimit, Equals, grp2.MemoryLimit)
   163  	c.Check(grp3.ParentGroup, Equals, grp2.ParentGroup)
   164  	c.Check(grp3.Snaps, DeepEquals, grp2.Snaps)
   165  	c.Check(newGrps, HasLen, 2)
   166  	c.Check(newGrps["foogroup"].SubGroups, DeepEquals, []string{"group-2"})
   167  
   168  	// and now we see both in the state
   169  	quotaMap, err = internal.AllQuotas(st)
   170  	c.Assert(err, IsNil)
   171  	c.Assert(quotaMap, DeepEquals, map[string]*quota.Group{
   172  		"foogroup": newGrps["foogroup"],
   173  		"group-2":  grp3,
   174  	})
   175  }