summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <michael.vogt@gmail.com>2015-12-03 16:11:24 +0100
committerMichael Vogt <michael.vogt@gmail.com>2015-12-03 16:11:24 +0100
commit83acb8febd24d88e0191edb29767a592f264d782 (patch)
tree8c03a2403073717925225f5b83d1a3fbd303b1b5
parentabb061b83b8041bd00a75f464daac0536543f7b9 (diff)
parente661aae17453c7f34aa3649f1da2c98b744de2ad (diff)
Merge pull request #211 from zyga/caps-api-remove-capability
Caps api remove capability
-rw-r--r--caps/repo.go12
-rw-r--r--caps/repo_test.go7
-rw-r--r--client/caps.go16
-rw-r--r--client/client_test.go32
-rw-r--r--cmd/snap/cmd_remove_cap.go50
-rw-r--r--daemon/api.go19
-rw-r--r--daemon/api_test.go34
-rw-r--r--po/snappy.pot8
8 files changed, 177 insertions, 1 deletions
diff --git a/caps/repo.go b/caps/repo.go
index 534e4d5f73..3e3cdf8c16 100644
--- a/caps/repo.go
+++ b/caps/repo.go
@@ -84,6 +84,9 @@ func (r *Repository) hasType(t *Type) bool {
// Type finds and returns the Type with the given name or nil if
// it's not found
func (r *Repository) Type(name string) *Type {
+ r.m.Lock()
+ defer r.m.Unlock()
+
for _, t := range r.types {
if t.Name == name {
return t
@@ -92,6 +95,15 @@ func (r *Repository) Type(name string) *Type {
return nil
}
+// Capability finds and returns the Capability with the given name or nil if it
+// is not found.
+func (r *Repository) Capability(name string) *Capability {
+ r.m.Lock()
+ defer r.m.Unlock()
+
+ return r.caps[name]
+}
+
// AddType adds a capability type to the repository.
// It's an error to add the same capability type more than once.
func (r *Repository) AddType(t *Type) error {
diff --git a/caps/repo_test.go b/caps/repo_test.go
index 069de6fe4f..9776471244 100644
--- a/caps/repo_test.go
+++ b/caps/repo_test.go
@@ -152,6 +152,13 @@ func (s *RepositorySuite) TestType(c *C) {
c.Assert(s.testRepo.Type(testType.Name), Equals, testType)
}
+func (s *RepositorySuite) TestCapability(c *C) {
+ err := s.testRepo.Add(testCapability)
+ c.Assert(err, IsNil)
+ c.Assert(s.emptyRepo.Capability(testCapability.Name), IsNil)
+ c.Assert(s.testRepo.Capability(testCapability.Name), Equals, testCapability)
+}
+
func (s *RepositorySuite) TestHasType(c *C) {
// hasType works as expected when the object is exactly the one that was
// added earlier.
diff --git a/client/caps.go b/client/caps.go
index bd4fa4c66e..da6e3aea74 100644
--- a/client/caps.go
+++ b/client/caps.go
@@ -83,3 +83,19 @@ func (client *Client) AddCapability(c *Capability) error {
}
return nil
}
+
+// RemoveCapability removes one capability from the system
+func (client *Client) RemoveCapability(name string) error {
+ errPrefix := "cannot remove capability"
+ var rsp response
+ if err := client.do("DELETE", fmt.Sprintf("/1.0/capabilities/%s", name), nil, &rsp); err != nil {
+ return err
+ }
+ if err := rsp.err(); err != nil {
+ return err
+ }
+ if rsp.Type != "sync" {
+ return fmt.Errorf("%s: expected sync response, got %q", errPrefix, rsp.Type)
+ }
+ return nil
+}
diff --git a/client/client_test.go b/client/client_test.go
index 639da3a265..421cc57638 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -155,6 +155,8 @@ func (cs *clientSuite) TestClientCapabilities(c *check.C) {
Attrs: map[string]string{"k": "v"},
},
})
+ c.Check(cs.req.Method, check.Equals, "GET")
+ c.Check(cs.req.URL.Path, check.Equals, "/1.0/capabilities")
}
func (cs *clientSuite) TestClientAddCapability(c *check.C) {
@@ -183,4 +185,34 @@ func (cs *clientSuite) TestClientAddCapability(c *check.C) {
"k": "v",
},
})
+ c.Check(cs.req.Method, check.Equals, "POST")
+ c.Check(cs.req.URL.Path, check.Equals, "/1.0/capabilities")
+}
+
+func (cs *clientSuite) TestClientRemoveCapabilityOk(c *check.C) {
+ cs.rsp = `{
+ "type": "sync",
+ "result": { }
+ }`
+ err := cs.cli.RemoveCapability("n")
+ c.Check(err, check.IsNil)
+ c.Check(cs.req.Body, check.IsNil)
+ c.Check(cs.req.Method, check.Equals, "DELETE")
+ c.Check(cs.req.URL.Path, check.Equals, "/1.0/capabilities/n")
+}
+
+func (cs *clientSuite) TestClientRemoveCapabilityNotFound(c *check.C) {
+ cs.rsp = `{
+ "status": "Not Found",
+ "status_code": 404,
+ "type": "error",
+ "result": {
+ "str": "can't remove capability \"n\", no such capability"
+ }
+ }`
+ err := cs.cli.RemoveCapability("n")
+ c.Check(err, check.ErrorMatches, `can't remove capability \"n\", no such capability`)
+ c.Check(cs.req.Body, check.IsNil)
+ c.Check(cs.req.Method, check.Equals, "DELETE")
+ c.Check(cs.req.URL.Path, check.Equals, "/1.0/capabilities/n")
}
diff --git a/cmd/snap/cmd_remove_cap.go b/cmd/snap/cmd_remove_cap.go
new file mode 100644
index 0000000000..4c6403ead3
--- /dev/null
+++ b/cmd/snap/cmd_remove_cap.go
@@ -0,0 +1,50 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2014-2015 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 (
+ "github.com/ubuntu-core/snappy/client"
+ "github.com/ubuntu-core/snappy/i18n"
+ "github.com/ubuntu-core/snappy/logger"
+)
+
+type removeCapOptions struct {
+ Name string `positional-arg-name:"name" description:"unique capability name"`
+}
+
+type cmdRemoveCap struct {
+ removeCapOptions `positional-args:"true" required:"true"`
+}
+
+var (
+ shortRemoveCapHelp = i18n.G("Remove a capability from the system")
+ longRemoveCapHelp = i18n.G("This command removes a capability from the system")
+)
+
+func init() {
+ _, err := parser.AddCommand("remove-cap", shortRemoveCapHelp, longRemoveCapHelp, &cmdRemoveCap{})
+ if err != nil {
+ logger.Panicf("unable to add remove-cap command: %v", err)
+ }
+}
+
+func (x *cmdRemoveCap) Execute(args []string) error {
+ return client.New().RemoveCapability(x.Name)
+}
diff --git a/daemon/api.go b/daemon/api.go
index 6a214a24f1..535a47f5f6 100644
--- a/daemon/api.go
+++ b/daemon/api.go
@@ -61,6 +61,7 @@ var api = []*Command{
packageSvcLogsCmd,
operationCmd,
capabilitiesCmd,
+ capabilityCmd,
}
var (
@@ -140,6 +141,11 @@ var (
GET: getCapabilities,
POST: addCapability,
}
+
+ capabilityCmd = &Command{
+ Path: "/1.0/capabilities/{name}",
+ DELETE: deleteCapability,
+ }
)
func v1Get(c *Command, r *http.Request) Response {
@@ -950,3 +956,16 @@ func addCapability(c *Command, r *http.Request) Response {
},
}
}
+
+func deleteCapability(c *Command, r *http.Request) Response {
+ name := muxVars(r)["name"]
+ err := c.d.capRepo.Remove(name)
+ switch err.(type) {
+ case nil:
+ return SyncResponse(nil)
+ case *caps.NotFoundError:
+ return NotFound(err, "can't remove capability")
+ default:
+ return InternalError(err, "")
+ }
+}
diff --git a/daemon/api_test.go b/daemon/api_test.go
index 4e37238ec8..fb2f1033fa 100644
--- a/daemon/api_test.go
+++ b/daemon/api_test.go
@@ -1325,3 +1325,37 @@ func (s *apiSuite) TestAddCapabilitiesNotACapability(c *check.C) {
// Verify (internal)
c.Check(d.capRepo.All(), check.HasLen, 0)
}
+
+func (s *apiSuite) TestDeleteCapabilityGood(c *check.C) {
+ // Setup
+ d := newTestDaemon()
+ t := &caps.Type{Name: "test"}
+ err := d.capRepo.AddType(t)
+ c.Assert(err, check.IsNil)
+ cap := &caps.Capability{Name: "name", Type: t}
+ err = d.capRepo.Add(cap)
+ c.Assert(err, check.IsNil)
+ s.vars = map[string]string{"name": "name"}
+ // Execute
+ rsp := deleteCapability(capabilityCmd, nil).Self(nil, nil).(*resp)
+ // Verify (external)
+ c.Check(rsp.Type, check.Equals, ResponseTypeSync)
+ c.Check(rsp.Status, check.Equals, http.StatusOK)
+ // Verify (internal)
+ c.Check(d.capRepo.Capability(cap.Name), check.IsNil)
+}
+
+func (s *apiSuite) TestDeleteCapabilityNotFound(c *check.C) {
+ // Setup
+ d := newTestDaemon()
+ before := d.capRepo.All()
+ s.vars = map[string]string{"name": "name"}
+ // Execute
+ rsp := deleteCapability(capabilityCmd, nil).Self(nil, nil).(*resp)
+ // Verify (external)
+ c.Check(rsp.Type, check.Equals, ResponseTypeError)
+ c.Check(rsp.Status, check.Equals, http.StatusNotFound)
+ // Verify (internal)
+ after := d.capRepo.All()
+ c.Check(before, check.DeepEquals, after)
+}
diff --git a/po/snappy.pot b/po/snappy.pot
index 374f836df4..46cbee0919 100644
--- a/po/snappy.pot
+++ b/po/snappy.pot
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: snappy\n"
"Report-Msgid-Bugs-To: snappy-devel@lists.ubuntu.com\n"
- "POT-Creation-Date: 2015-12-02 09:28+0100\n"
+ "POT-Creation-Date: 2015-12-02 11:46+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -249,6 +249,9 @@ msgstr ""
msgid "Rebooting to satisfy updates for %s\n"
msgstr ""
+msgid "Remove a capability from the system"
+msgstr ""
+
msgid "Remove a snapp part"
msgstr ""
@@ -376,6 +379,9 @@ msgstr ""
msgid "This command logs the given username into the store"
msgstr ""
+msgid "This command removes a capability from the system"
+msgstr ""
+
msgid "This command removes access of a specific hardware device (e.g. /dev/ttyUSB0) for an installed package."
msgstr ""