Beispiel #1
0
func (s *setSuite) TestCommand(c *C) {
	stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"set", "foo=bar", "baz=qux"})
	c.Check(err, IsNil)
	c.Check(string(stdout), Equals, "")
	c.Check(string(stderr), Equals, "")

	// Verify that the previous set doesn't modify the global state
	s.mockContext.State().Lock()
	transaction, err := configstate.NewTransaction(s.mockContext.State())
	s.mockContext.State().Unlock()
	c.Check(err, IsNil)
	var value string
	c.Check(transaction.Get("test-snap", "foo", &value), ErrorMatches, ".*snap.*has no.*configuration.*")
	c.Check(transaction.Get("test-snap", "baz", &value), ErrorMatches, ".*snap.*has no.*configuration.*")

	// Notify the context that we're done. This should save the config.
	s.mockContext.Lock()
	defer s.mockContext.Unlock()
	c.Check(s.mockContext.Done(), IsNil)

	// Verify that the global config has been updated.
	transaction, err = configstate.NewTransaction(s.mockContext.State())
	c.Check(err, IsNil)
	c.Check(transaction.Get("test-snap", "foo", &value), IsNil)
	c.Check(value, Equals, "bar")
	c.Check(transaction.Get("test-snap", "baz", &value), IsNil)
	c.Check(value, Equals, "qux")
}
Beispiel #2
0
func (s *setSuite) TestCommandSavesDeltasOnly(c *C) {
	// Setup an initial configuration
	s.mockContext.State().Lock()
	transaction, err := configstate.NewTransaction(s.mockContext.State())
	c.Check(err, IsNil)
	transaction.Set("test-snap", "test-key1", "test-value1")
	transaction.Set("test-snap", "test-key2", "test-value2")
	transaction.Commit()
	s.mockContext.State().Unlock()

	stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"set", "test-key2=test-value3"})
	c.Check(err, IsNil)
	c.Check(string(stdout), Equals, "")
	c.Check(string(stderr), Equals, "")

	// Notify the context that we're done. This should save the config.
	s.mockContext.Lock()
	defer s.mockContext.Unlock()
	c.Check(s.mockContext.Done(), IsNil)

	// Verify that the global config has been updated, but only test-key2
	transaction, err = configstate.NewTransaction(s.mockContext.State())
	c.Check(err, IsNil)
	var value string
	c.Check(transaction.Get("test-snap", "test-key1", &value), IsNil)
	c.Check(value, Equals, "test-value1")
	c.Check(transaction.Get("test-snap", "test-key2", &value), IsNil)
	c.Check(value, Equals, "test-value3")
}
Beispiel #3
0
func (s *transactionSuite) TestCommitOnlyCommitsChanges(c *C) {
	// Set the initial config
	s.state.Lock()
	defer s.state.Unlock()
	c.Check(s.transaction.Set("test-snap", "foo", "bar"), IsNil)
	s.transaction.Commit()

	// Create two new transactions
	transaction1, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)
	transaction2, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)

	// transaction1 will change the configuration item that is already present.
	c.Check(transaction1.Set("test-snap", "foo", "baz"), IsNil)
	transaction1.Commit()

	// transaction2 will add a new configuration item.
	c.Check(transaction2.Set("test-snap", "qux", "quux"), IsNil)
	transaction2.Commit()

	// Now verify that the change made by both transactions actually took place
	// (i.e. transaction1's change was not overridden by the old data in
	// transaction2).
	transaction, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)

	var value string
	c.Check(transaction.Get("test-snap", "foo", &value), IsNil)
	c.Check(value, Equals, "baz", Commentf("Expected 'test-snap' value for 'foo' to be set by transaction1"))

	c.Check(transaction.Get("test-snap", "qux", &value), IsNil)
	c.Check(value, Equals, "quux", Commentf("Expected 'test-snap' value for 'qux' to be set by transaction2"))
}
Beispiel #4
0
func (s *getSuite) TestGetTests(c *C) {
	for _, test := range getTests {
		c.Logf("Test: %s", test.args)

		mockHandler := hooktest.NewMockHandler()

		state := state.New(nil)
		state.Lock()

		task := state.NewTask("test-task", "my test task")
		setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"}

		var err error
		mockContext, err := hookstate.NewContext(task, setup, mockHandler)
		c.Check(err, IsNil)

		// Initialize configuration
		t := configstate.NewTransaction(state)
		t.Set("test-snap", "test-key1", "test-value1")
		t.Set("test-snap", "test-key2", 2)
		t.Commit()

		state.Unlock()

		stdout, stderr, err := ctlcmd.Run(mockContext, strings.Fields(test.args))
		if test.error != "" {
			c.Check(err, ErrorMatches, test.error)
		} else {
			c.Check(err, IsNil)
			c.Check(string(stderr), Equals, "")
			c.Check(string(stdout), Equals, test.stdout)
		}
	}
}
Beispiel #5
0
func getSnapConf(c *Command, r *http.Request, user *auth.UserState) Response {
	vars := muxVars(r)
	snapName := vars["name"]

	keys := strings.Split(r.URL.Query().Get("keys"), ",")
	if len(keys) == 0 {
		return BadRequest("cannot obtain configuration: no keys supplied")
	}

	s := c.d.overlord.State()
	s.Lock()
	transaction, err := configstate.NewTransaction(s)
	s.Unlock()
	if err != nil {
		return BadRequest("cannot create transaction: %s", err)
	}

	currentConfValues := make(map[string]interface{})
	for _, key := range keys {
		var value interface{}
		if err := transaction.Get(snapName, key, &value); err != nil {
			return BadRequest("%s", err)
		}

		currentConfValues[key] = value
	}

	return SyncResponse(currentConfValues, nil)
}
Beispiel #6
0
func (s *transactionSuite) SetUpTest(c *C) {
	s.state = state.New(nil)
	var err error
	s.state.Lock()
	defer s.state.Unlock()
	s.transaction, err = configstate.NewTransaction(s.state)
	c.Check(err, IsNil)
}
Beispiel #7
0
func (s *transactionSuite) TestSetDoesNotTouchState(c *C) {
	c.Check(s.transaction.Set("test-snap", "foo", "bar"), IsNil)

	// Create a new transaction to grab a new snapshot of the state
	s.state.Lock()
	defer s.state.Unlock()
	transaction, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)
	var value string
	err = transaction.Get("test-snap", "foo", &value)
	c.Check(err, NotNil, Commentf("Expected config set by first transaction to not be saved"))
}
Beispiel #8
0
func (s *transactionSuite) TestIsolationFromOtherTransactions(c *C) {
	// Set the initial config
	s.state.Lock()
	defer s.state.Unlock()
	c.Check(s.transaction.Set("test-snap", "foo", "initial"), IsNil)
	s.transaction.Commit()

	// Create two new transactions
	transaction1, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)
	transaction2, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)

	// Change the config in one
	c.Check(transaction1.Set("test-snap", "foo", "updated"), IsNil)
	transaction1.Commit()

	// Verify that the other transaction doesn't see the changes
	var value string
	c.Check(transaction2.Get("test-snap", "foo", &value), IsNil)
	c.Check(value, Equals, "initial", Commentf("Expected transaction2 to be isolated from transaction1"))
}
Beispiel #9
0
func (s *transactionSuite) TestCommit(c *C) {
	s.state.Lock()
	defer s.state.Unlock()
	c.Check(s.transaction.Set("test-snap", "foo", "bar"), IsNil)
	s.transaction.Commit()

	// Create a new transaction to grab a new snapshot of the state
	transaction, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)
	var value string
	err = transaction.Get("test-snap", "foo", &value)
	c.Check(err, IsNil, Commentf("Expected config set by first transaction to be saved"))
	c.Check(value, Equals, "bar")
}
Beispiel #10
0
func (s *transactionSuite) TestGetOriginalEvenWithCachedWrites(c *C) {
	// Set the initial config
	s.state.Lock()
	defer s.state.Unlock()
	c.Check(s.transaction.Set("test-snap", "foo", "bar"), IsNil)
	s.transaction.Commit()

	transaction, err := configstate.NewTransaction(s.state)
	c.Check(err, IsNil)
	c.Check(transaction.Set("test-snap", "baz", "qux"), IsNil)

	// Now get both the cached write as well as the initial config
	var value string
	c.Check(transaction.Get("test-snap", "foo", &value), IsNil)
	c.Check(value, Equals, "bar")
	c.Check(transaction.Get("test-snap", "baz", &value), IsNil)
	c.Check(value, Equals, "qux")
}
Beispiel #11
0
func (s *getSuite) SetUpTest(c *C) {
	s.mockHandler = hooktest.NewMockHandler()

	state := state.New(nil)
	state.Lock()
	defer state.Unlock()

	task := state.NewTask("test-task", "my test task")
	setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"}

	var err error
	s.mockContext, err = hookstate.NewContext(task, setup, s.mockHandler)
	c.Assert(err, IsNil)

	// Initialize configuration
	transaction := configstate.NewTransaction(state)
	transaction.Set("test-snap", "initial-key", "initial-value")
	transaction.Commit()
}
Beispiel #12
0
func (s *transactionSuite) TestGetUnmarshalError(c *C) {
	s.state.Lock()
	defer s.state.Unlock()
	c.Check(s.transaction.Set("test-snap", "foo", "good"), IsNil)
	s.transaction.Commit()

	transaction := configstate.NewTransaction(s.state)
	c.Check(transaction.Set("test-snap", "foo", "break"), IsNil)

	// Pristine state is good, value in the transaction breaks.
	broken := brokenType{`"break"`}
	err := transaction.Get("test-snap", "foo", &broken)
	c.Assert(err, ErrorMatches, ".*BAM!.*")

	// Pristine state breaks, nothing in the transaction.
	transaction.Commit()
	err = transaction.Get("test-snap", "foo", &broken)
	c.Assert(err, ErrorMatches, ".*BAM!.*")
}
Beispiel #13
0
func getTransaction(context *hookstate.Context) (*configstate.Transaction, error) {
	context.Lock()
	defer context.Unlock()

	// Extract the transaction from the context. If none, make one and cache it
	// in the context.
	transaction, ok := context.Cached(cachedTransaction{}).(*configstate.Transaction)
	if !ok {
		var err error
		transaction, err = configstate.NewTransaction(context.State())
		if err != nil {
			return nil, fmt.Errorf("cannot create transaction: %s", err)
		}

		context.OnDone(func() error {
			transaction.Commit()
			return nil
		})

		context.Cache(cachedTransaction{}, transaction)
	}

	return transaction, nil
}
Beispiel #14
0
func getSerialRequestConfig(t *state.Task) (*serialRequestConfig, error) {
	gadgetInfo, err := snapstate.GadgetInfo(t.State())
	if err != nil {
		return nil, fmt.Errorf("cannot find gadget snap and its name: %v", err)
	}
	gadgetName := gadgetInfo.Name()

	tr := configstate.NewTransaction(t.State())
	var svcURL string
	err = tr.GetMaybe(gadgetName, "device-service.url", &svcURL)
	if err != nil {
		return nil, err
	}

	if svcURL != "" {
		baseURL, err := url.Parse(svcURL)
		if err != nil {
			return nil, fmt.Errorf("cannot parse device registration base URL %q: %v", svcURL, err)
		}

		var headers map[string]string
		err = tr.GetMaybe(gadgetName, "device-service.headers", &headers)
		if err != nil {
			return nil, err
		}

		cfg := serialRequestConfig{
			headers: headers,
		}

		reqIDURL, err := baseURL.Parse("request-id")
		if err != nil {
			return nil, fmt.Errorf("cannot build /request-id URL from %v: %v", baseURL, err)
		}
		cfg.requestIDURL = reqIDURL.String()

		var bodyStr string
		err = tr.GetMaybe(gadgetName, "registration.body", &bodyStr)
		if err != nil {
			return nil, err
		}

		cfg.body = []byte(bodyStr)

		serialURL, err := baseURL.Parse("serial")
		if err != nil {
			return nil, fmt.Errorf("cannot build /serial URL from %v: %v", baseURL, err)
		}
		cfg.serialRequestURL = serialURL.String()

		var proposedSerial string
		err = tr.GetMaybe(gadgetName, "registration.proposed-serial", &proposedSerial)
		if err != nil {
			return nil, err
		}
		cfg.proposedSerial = proposedSerial

		return &cfg, nil
	}

	return &serialRequestConfig{
		requestIDURL:     requestIDURL,
		serialRequestURL: serialRequestURL,
	}, nil
}
Beispiel #15
0
func (s *transactionSuite) SetUpTest(c *C) {
	s.state = state.New(nil)
	s.state.Lock()
	defer s.state.Unlock()
	s.transaction = configstate.NewTransaction(s.state)
}
Beispiel #16
0
func (s *transactionSuite) TestSetGet(c *C) {
	s.state.Lock()
	defer s.state.Unlock()

	for _, test := range setGetTests {
		c.Logf("-----")
		s.state.Set("config", map[string]interface{}{})
		t := configstate.NewTransaction(s.state)
		snap := "core"
		for _, op := range test {
			c.Logf("%s", op)
			switch op.kind() {
			case "set":
				for k, v := range op.args() {
					err := t.Set(snap, k, v)
					if op.fails() {
						c.Assert(err, ErrorMatches, op.error())
					} else {
						c.Assert(err, IsNil)
					}
				}

			case "get":
				for k, expected := range op.args() {
					var obtained interface{}
					err := t.Get(snap, k, &obtained)
					if op.fails() {
						c.Assert(err, ErrorMatches, op.error())
						var nothing interface{}
						c.Assert(t.GetMaybe(snap, k, &nothing), ErrorMatches, op.error())
						c.Assert(nothing, IsNil)
						continue
					}
					if expected == "-" {
						if !configstate.IsNoOption(err) {
							c.Fatalf("Expected %q key to not exist, but it has value %v", k, obtained)
						}
						c.Assert(err, ErrorMatches, fmt.Sprintf("snap %q has no %q configuration option", snap, k))
						var nothing interface{}
						c.Assert(t.GetMaybe(snap, k, &nothing), IsNil)
						c.Assert(nothing, IsNil)
						continue
					}
					c.Assert(err, IsNil)
					c.Assert(obtained, DeepEquals, expected)

					obtained = nil
					c.Assert(t.GetMaybe(snap, k, &obtained), IsNil)
					c.Assert(obtained, DeepEquals, expected)
				}

			case "commit":
				t.Commit()

			case "setunder":
				var config map[string]map[string]interface{}
				s.state.Get("config", &config)
				if config == nil {
					config = make(map[string]map[string]interface{})
				}
				if config[snap] == nil {
					config[snap] = make(map[string]interface{})
				}
				for k, v := range op.args() {
					if v == "-" {
						delete(config[snap], k)
						if len(config[snap]) == 0 {
							delete(config[snap], snap)
						}
					} else {
						config[snap][k] = v
					}
				}
				s.state.Set("config", config)

			case "getunder":
				var config map[string]map[string]interface{}
				s.state.Get("config", &config)
				for k, expected := range op.args() {
					obtained, ok := config[snap][k]
					if expected == "-" {
						if ok {
							c.Fatalf("Expected %q key to not exist, but it has value %v", k, obtained)
						}
						continue
					}
					c.Assert(obtained, DeepEquals, expected)
				}

			default:
				panic("unknown test op kind: " + op.kind())
			}
		}
	}
}