Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
hooks: commands for controlling own services from snapctl #3852
Merged
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
6e24531
Added servicectl and moved a helper function for dealing with service…
stolowski 15cf826
Added snapctl start/stop/restart.
stolowski 80eeba3
Merge branch 'master' into servicectl
stolowski 5e3f792
Spread tests.
stolowski eeb0f27
Unit test.
stolowski 31c6117
More unit tests.
stolowski e52d986
One more test.
stolowski 2a8dadd
Merge branch 'master' into servicectl
stolowski 7899865
Merge branch 'master' into servicectl
stolowski 56d927e
Use configure's hook timeout as a base for service commands timeout.
stolowski 9b23d3e
Locking fix.
stolowski 3abd31c
Simplified getServiceInfos (thanks John!).
stolowski d8df1f1
Review comments.
stolowski 30001c2
Merge branch 'master' into servicectl
stolowski b4c9ed8
Merge branch 'master' into servicectl
stolowski 169857a
Review comments.
stolowski e9eaaa4
Renamed to servicestate.Change.
stolowski e897612
Rename servicetl file to servicestate.
stolowski
Jump to file or symbol
Failed to load files and symbols.
| @@ -0,0 +1,100 @@ | ||
| +// -*- Mode: Go; indent-tabs-mode: t -*- | ||
| + | ||
| +/* | ||
| + * Copyright (C) 2017 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 ctlcmd | ||
| + | ||
| +import ( | ||
| + "fmt" | ||
| + "strings" | ||
| + "time" | ||
| + | ||
| + "github.com/snapcore/snapd/i18n" | ||
| + "github.com/snapcore/snapd/overlord/configstate" | ||
| + "github.com/snapcore/snapd/overlord/hookstate" | ||
| + "github.com/snapcore/snapd/overlord/servicestate" | ||
| + "github.com/snapcore/snapd/overlord/snapstate" | ||
| + "github.com/snapcore/snapd/overlord/state" | ||
| + "github.com/snapcore/snapd/snap" | ||
| +) | ||
| + | ||
| +func getServiceInfos(st *state.State, snapName string, serviceNames []string) ([]*snap.AppInfo, error) { | ||
| + st.Lock() | ||
| + defer st.Unlock() | ||
| + | ||
| + var snapst snapstate.SnapState | ||
| + if err := snapstate.Get(st, snapName, &snapst); err != nil { | ||
| + return nil, err | ||
| + } | ||
| + | ||
| + info, err := snapst.CurrentInfo() | ||
| + if err != nil { | ||
| + return nil, err | ||
| + } | ||
| + | ||
| + var svcs []*snap.AppInfo | ||
| + for _, svcName := range serviceNames { | ||
| + if svcName == snapName { | ||
| + // all the services | ||
| + return info.Services(), nil | ||
| + } | ||
| + if !strings.HasPrefix(svcName, snapName+".") { | ||
| + return nil, fmt.Errorf(i18n.G("unknown service: %q"), svcName) | ||
| + } | ||
| + // this doesn't support service aliases | ||
| + app, ok := info.Apps[svcName[1+len(snapName):]] | ||
| + if !(ok && app.IsService()) { | ||
| + return nil, fmt.Errorf(i18n.G("unknown service: %q"), svcName) | ||
| + } | ||
| + svcs = append(svcs, app) | ||
| + } | ||
| + | ||
| + return svcs, nil | ||
| +} | ||
| + | ||
| +var servicechangeImpl = servicestate.Change | ||
| + | ||
| +func runServiceCommand(context *hookstate.Context, inst *servicestate.Instruction, serviceNames []string) error { | ||
| + if context == nil { | ||
| + return fmt.Errorf(i18n.G("cannot %s without a context"), inst.Action) | ||
| + } | ||
| + | ||
| + st := context.State() | ||
| + appInfos, err := getServiceInfos(st, context.SnapName(), serviceNames) | ||
| + if err != nil { | ||
| + return err | ||
| + } | ||
| + | ||
| + chg, err := servicechangeImpl(st, appInfos, inst) | ||
| + if err != nil { | ||
| + return err | ||
| + } | ||
| + | ||
| + st.Lock() | ||
| + st.EnsureBefore(0) | ||
| + st.Unlock() | ||
| + | ||
| + select { | ||
| + case <-chg.Ready(): | ||
| + st.Lock() | ||
| + defer st.Unlock() | ||
| + return chg.Err() | ||
| + case <-time.After(configstate.ConfigureHookTimeout() / 2): | ||
| + return fmt.Errorf("%s command is taking too long", inst.Action) | ||
| + } | ||
| +} |
| @@ -0,0 +1,53 @@ | ||
| +// -*- Mode: Go; indent-tabs-mode: t -*- | ||
| + | ||
| +/* | ||
| + * Copyright (C) 2016-2017 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 ctlcmd | ||
| + | ||
| +import ( | ||
| + "github.com/snapcore/snapd/client" | ||
| + "github.com/snapcore/snapd/i18n" | ||
| + "github.com/snapcore/snapd/overlord/servicestate" | ||
| +) | ||
| + | ||
| +var ( | ||
| + shortRestartHelp = i18n.G("Restart services") | ||
| +) | ||
| + | ||
| +func init() { | ||
| + addCommand("restart", shortRestartHelp, "", func() command { return &restartCommand{} }) | ||
| +} | ||
| + | ||
| +type restartCommand struct { | ||
| + baseCommand | ||
| + Positional struct { | ||
| + ServiceNames []string `positional-arg-name:"<service>" required:"yes"` | ||
| + } `positional-args:"yes" required:"yes"` | ||
| + Reload bool `long:"reload"` | ||
| +} | ||
| + | ||
| +func (c *restartCommand) Execute(args []string) error { | ||
| + inst := servicestate.Instruction{ | ||
| + Action: "restart", | ||
| + Names: c.Positional.ServiceNames, | ||
| + RestartOptions: client.RestartOptions{ | ||
| + Reload: c.Reload, | ||
| + }, | ||
| + } | ||
| + return runServiceCommand(c.context(), &inst, c.Positional.ServiceNames) | ||
| +} |
Oops, something went wrong.