File: cmd_set.go

package info (click to toggle)
snapd 2.72-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 80,412 kB
  • sloc: sh: 16,506; ansic: 16,211; python: 11,213; makefile: 1,919; exp: 190; awk: 58; xml: 22
file content (147 lines) | stat: -rw-r--r-- 4,021 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 2016 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	"errors"
	"strings"

	"github.com/jessevdk/go-flags"

	"github.com/snapcore/snapd/client/clientutil"
	"github.com/snapcore/snapd/i18n"
)

var shortSetHelp = i18n.G("Change configuration options")
var longSetHelp = i18n.G(`
The set command changes the provided configuration options as requested.

    $ snap set snap-name username=frank password=$PASSWORD

All configuration changes are persisted at once, and only after the
snap's configuration hook returns successfully.

Nested values may be modified via a dotted path:

    $ snap set snap-name author.name=frank

Configuration option may be unset with exclamation mark:
    $ snap set snap-name author!
`)

var longConfdbSetHelp = i18n.G(`
If the first argument passed into set is a confdb identifier matching the
format <account-id>/<confdb>/<view>, set will use the confdb API. In this
case, the command sets the values as provided for the dot-separated view paths.
`)

type cmdSet struct {
	waitMixin
	Positional struct {
		Snap       installedSnapName
		ConfValues []string `required:"1"`
	} `positional-args:"yes" required:"yes"`

	Typed  bool `short:"t"`
	String bool `short:"s"`
}

func init() {
	if err := validateConfdbFeatureFlag(); err == nil {
		longSetHelp += longConfdbSetHelp
	}

	addCommand("set", shortSetHelp, longSetHelp, func() flags.Commander { return &cmdSet{} },
		waitDescs.also(map[string]string{
			// TRANSLATORS: This should not start with a lowercase letter.
			"t": i18n.G("Parse the value strictly as JSON document"),
			// TRANSLATORS: This should not start with a lowercase letter.
			"s": i18n.G("Parse the value as a string"),
		}), []argDesc{
			{
				name: "<snap>",
				// TRANSLATORS: This should not start with a lowercase letter.
				desc: i18n.G("The snap to configure (e.g. hello-world)"),
			}, {
				// TRANSLATORS: This needs to begin with < and end with >
				name: i18n.G("<conf value>"),
				// TRANSLATORS: This should not start with a lowercase letter.
				desc: i18n.G("Set (key=value) or unset (key!) configuration value"),
			},
		})
}

func (x *cmdSet) Execute([]string) error {
	if x.String && x.Typed {
		return errors.New(i18n.G("cannot use -t and -s together"))
	}

	opts := &clientutil.ParseConfigOptions{String: x.String, Typed: x.Typed}
	patchValues, _, err := clientutil.ParseConfigValues(x.Positional.ConfValues, opts)
	if err != nil {
		return err
	}

	snapName := string(x.Positional.Snap)
	var chgID string
	if isConfdbViewID(snapName) {
		if err := validateConfdbFeatureFlag(); err != nil {
			return err
		}

		// first argument is a confdbViewID, use the confdb API
		confdbViewID := snapName
		if err := validateConfdbViewID(confdbViewID); err != nil {
			return err
		}

		chgID, err = x.client.ConfdbSetViaView(confdbViewID, patchValues)
	} else {
		chgID, err = x.client.SetConf(snapName, patchValues)
	}

	if err != nil {
		return err
	}

	if _, err := x.wait(chgID); err != nil {
		if err == noWait {
			return nil
		}
		return err
	}

	return nil
}

func isConfdbViewID(s string) bool {
	return len(strings.Split(s, "/")) == 3
}

func validateConfdbViewID(id string) error {
	parts := strings.Split(id, "/")
	for _, part := range parts {
		if part == "" {
			return errors.New(i18n.G("confdb-schema view id must conform to format: <account-id>/<confdb-schema>/<view>"))
		}
	}

	return nil
}