Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Add initial capability package #65
Conversation
|
Repushed after fixing golint issues |
mvo5
reviewed
Nov 9, 2015
| +var validName *regexp.Regexp | ||
| + | ||
| +func init() { | ||
| + validName = regexp.MustCompile("^[a-z][a-z0-9-]+$") |
mvo5
Nov 9, 2015
Collaborator
You don't need to put this inside init(), you can write:
var validName = regexp.MustCompile("^[a-z][a-z0-9-]+$")
|
Applied suggestion from @mvo5 |
|
14:31 < roadmr> zyga: typo \o/ line 16 in capabilities.go has an extra leading space :P |
zyga
added some commits
Nov 9, 2015
niemeyer
reviewed
Nov 10, 2015
| + "regexp" | ||
| +) | ||
| + | ||
| +// Capability that expressess an instance Snappy Capability. |
niemeyer
Nov 10, 2015
Contributor
Not sure I understand the sentence.. perhaps something along the lines of:
// Capability holds information about a capability that a snap may request
// from a snappy system to do its job while running on it.
niemeyer
reviewed
Nov 10, 2015
| +// Capability that expressess an instance Snappy Capability. | ||
| +type Capability struct { | ||
| + // Name that is also used for programmatic access on command line and | ||
| + // at application runtime. This is constrained to [a-z][a-z0-9-]+ |
niemeyer
Nov 10, 2015
Contributor
// Name is a key that identifies the capability. It must be unique within its context, which
// may be either a snap or a snappy runtime.
niemeyer
reviewed
Nov 10, 2015
| + // at application runtime. This is constrained to [a-z][a-z0-9-]+ | ||
| + Name string | ||
| + // Label meant for humans. This is the English version of this label. | ||
| + // TODO: Add an i18n mechanism later. |
niemeyer
Nov 10, 2015
Contributor
// Label provides an optional title for the capability to help a human tell which physical
// device this capability is referring to. It might say "Front USB", or "Green Serial Port",
// for example.
Please drop the TODO item for now. The translation here is special, because the physical label may not match the interface language, so let's not encourage passers-by to spend time on this before we understand what we actually want to do.
niemeyer
reviewed
Nov 10, 2015
| + // TODO: Add an i18n mechanism later. | ||
| + Label string | ||
| + // CapabilityType describes a group of capabilities sharing some common | ||
| + // traits. In particular, this is where security bits are coming from. |
niemeyer
Nov 10, 2015
Contributor
// Type defines the type of this capability. The capability type defines the behavior allowed and
// expected from providers and consumers of that capability, and also which information should
// be exchanged by these parties.
niemeyer
reviewed
Nov 10, 2015
| +// All capability types are maintained with snappy source code. | ||
| +// In other words, there are no 3rd party capabilitites as they have | ||
| +// deep access to system security API promisses. | ||
| +type CapabilityType struct { |
niemeyer
Nov 10, 2015
Contributor
We can name this simply as Type. This is already within the caps package, so it ends up as caps.Type.
niemeyer
reviewed
Nov 10, 2015
| + * | ||
| + */ | ||
| + | ||
| +package capabilities |
niemeyer
Nov 10, 2015
Contributor
I suggest naming this caps, for short. We'll unfortunately take over this convenient variable name, but it'll be boring to type and read the full fledged word behind every variable, type, method, etc, that we define here.
niemeyer
reviewed
Nov 10, 2015
| + // as snaps will refer to capabilities types with a given name. Gadget | ||
| + // snaps will also provide mechanisms to create capabilities with a given | ||
| + // type name. | ||
| + Name string |
niemeyer
Nov 10, 2015
Contributor
Rather than a struct, I believe this should be simply:
// Type is the name of a capability type.
type Type string
We also don't need to re-describe this, since we just defined it inside Capability which is the more interesting type.
niemeyer
reviewed
Nov 10, 2015
| +// CapTypeFile is a basic capability vaguely expressing access to a specific | ||
| +// file. This single capability type is here just to help boostrap | ||
| +// the capability concept before we get to load capability interfaces from YAML. | ||
| +var CapTypeFile = CapabilityType{"file"} |
niemeyer
Nov 10, 2015
Contributor
The idea described in the comment sounds good, but we should already be moving in that direction. If capabilities are externally defined, we shouldn't have the concept of Go types here, since we need it to be extensible, which we can't do if we need to recompile snappy to introduce new types. This should probably be something along those lines:
const (
FileType Type = "file"
)
So we get caps.FileType valued as "file". This means if we load an external capability type named "file", we can simply do caps.Type("file") and end up in a similar place.
niemeyer
reviewed
Nov 10, 2015
| +var CapTypeFile = CapabilityType{"file"} | ||
| + | ||
| +// Regular expression describing correct identifiers | ||
| +var validName = regexp.MustCompile("^[a-z][a-z0-9-]+$") |
niemeyer
reviewed
Nov 10, 2015
| +var validName = regexp.MustCompile("^[a-z][a-z0-9-]+$") | ||
| + | ||
| +// NewCapability creates a new Capability object after validating arguments | ||
| +func NewCapability(Name, Label string, Type CapabilityType) (*Capability, error) { |
niemeyer
Nov 10, 2015
Contributor
I'd prefer to not have this until we're really making good use of it, because this:
caps.Capability{Name: name, Type: type}
is much more readable and more flexible than this:
caps.New(name, type)
niemeyer
reviewed
Nov 10, 2015
| +// A CapabilityRepository stores all known snappy capabilities and types | ||
| +type CapabilityRepository struct { | ||
| + // Map of capabilities, indexed by Capability.Name | ||
| + Caps map[string]*Capability |
niemeyer
Nov 10, 2015
Contributor
This should be caps.Repository, and we should try to make our implementations private whenever there's not a reason to expose it. In this case, you're designing a carefully crafted API, and the map here is just an implementation detail.
niemeyer
reviewed
Nov 10, 2015
| +} | ||
| + | ||
| +// NewCapabilityRepository creates an empty capability repository | ||
| +func NewCapabilityRepository() *CapabilityRepository { |
niemeyer
reviewed
Nov 10, 2015
| +// Capability names must be unique within the repository. | ||
| +// An error is returned if this constraint is violated. | ||
| +func (r *CapabilityRepository) Add(cap *Capability) error { | ||
| + if _, capPresent := r.Caps[cap.Name]; capPresent { |
niemeyer
Nov 10, 2015
Contributor
Conventionally (and FYI mainly since you're just joining the Go party), most trivial uses of this expression use "ok" as the bool variable.
niemeyer
reviewed
Nov 10, 2015
| +// An error is returned if this constraint is violated. | ||
| +func (r *CapabilityRepository) Add(cap *Capability) error { | ||
| + if _, capPresent := r.Caps[cap.Name]; capPresent { | ||
| + return errors.New("Capability with that name already exists") |
niemeyer
Nov 10, 2015
Contributor
Similarly, please start error sentences on lowercase, so we "Avoid: Messages: Like: This".
niemeyer
reviewed
Nov 10, 2015
| + | ||
| +// Remove a capability with a given name. | ||
| +// Removing a capability that doesn't exist silently does nothing | ||
| +func (r *CapabilityRepository) Remove(Name string) { |
niemeyer
reviewed
Nov 10, 2015
| +func (s *CapabilitySuite) TestNewCapabilityInvalidName(c *C) { | ||
| + cap, err := NewCapability("name with space", "label", CapTypeFile) | ||
| + c.Assert(cap, IsNil) | ||
| + c.Assert(err, ErrorMatches, "Name is not a valid identifier") |
niemeyer
Nov 10, 2015
Contributor
If we go with using the capability struct for now, this may become caps.ValidateName for the time being.
niemeyer
reviewed
Nov 10, 2015
| + | ||
| +func (s *CapabilitySuite) TestNewRepository(c *C) { | ||
| + repo := NewCapabilityRepository() | ||
| + c.Assert(len(repo.Caps), Equals, 0) |
niemeyer
reviewed
Nov 10, 2015
| +} | ||
| + | ||
| +func (s *CapabilitySuite) TestAddCapabilityAllOk(c *C) { | ||
| + cap, capErr := NewCapability("name", "label", CapTypeFile) |
niemeyer
reviewed
Nov 10, 2015
| +func (s *CapabilitySuite) TestAddClash(c *C) { | ||
| + repo := NewCapabilityRepository() | ||
| + cap1, cap1Err := NewCapability("name", "label 1", CapTypeFile) | ||
| + cap2, cap2Err := NewCapability("name", "label 2", CapTypeFile) |
niemeyer
Nov 10, 2015
Contributor
s/cap1Err/err/, s/cap2Err/err, and then assert in place instead of postponing it.
niemeyer
reviewed
Nov 10, 2015
| + c.Assert(cap1Err, IsNil) | ||
| + c.Assert(cap2Err, IsNil) | ||
| + err1 := repo.Add(cap1) | ||
| + err2 := repo.Add(cap2) |
niemeyer
reviewed
Nov 10, 2015
| + | ||
| +func (s *CapabilitySuite) TestAdd(c *C) { | ||
| + repo := NewCapabilityRepository() | ||
| + cap, _ := NewCapability("name", "label", CapTypeFile) |
niemeyer
reviewed
Nov 10, 2015
| +} | ||
| + | ||
| +func (s *CapabilitySuite) TestRemove(c *C) { | ||
| + cap, _ := NewCapability("name", "label", CapTypeFile) |
niemeyer
reviewed
Nov 10, 2015
| @@ -41,6 +42,7 @@ type Daemon struct { | ||
| listener net.Listener | ||
| tomb tomb.Tomb | ||
| router *mux.Router | ||
| + capRepo *capabilities.CapabilityRepository |
chipaca
Nov 10, 2015
Member
@zyga checked with me before putting it here; I'll repeat here what I told him: without knowing how it's going to get used it's hard to give an answer with certainty, but you can't go wrong putting it in the daemon, as there'll only be one instance of it and every command has access to it.
niemeyer
reviewed
Nov 10, 2015
| + case reflect.Slice, reflect.Array: | ||
| + // Ensure that type of elements in haystack is compatible with needle | ||
| + if needleV := reflect.ValueOf(needle); haystackV.Type().Elem() != needleV.Type() { | ||
| + panic(fmt.Sprintf("haystack contains items of type %s but needle is a %s", |
niemeyer
Nov 10, 2015
Contributor
This should return the error in the error string, rather than being a panic. It's the only reason why the that result exists. Have a look at use cases in checkers.go in check.v1 for examples.
niemeyer
Nov 10, 2015
Contributor
Also, let's please use generic names for the error message, specifically: "container has items of type %s but expected element is a %s". go-check has its own names for those variables, the call site will have its own local variable names too, so adding new analogies here won't help much.
niemeyer
reviewed
Nov 10, 2015
| + // Ensure that type of elements in haystack is compatible with needle | ||
| + if needleV := reflect.ValueOf(needle); haystackV.Type().Elem() != needleV.Type() { | ||
| + panic(fmt.Sprintf("haystack contains items of type %s but needle is a %s", | ||
| + haystackV.Type().Elem(), needleV.Type())) |
niemeyer
reviewed
Nov 10, 2015
| + } | ||
| + return false, "" | ||
| + case reflect.String: | ||
| + // When haystack is a string, we expect needle to be a string as well |
niemeyer
Nov 10, 2015
Contributor
If it's not, it should error out politely per above rather than panic.
niemeyer
reviewed
Nov 10, 2015
| + haystack := params[0].(string) | ||
| + return strings.Contains(haystack, needle), "" | ||
| + default: | ||
| + panic(fmt.Sprintf("haystack is of unsupported type %T", params[0])) |
niemeyer
Nov 10, 2015
Contributor
This should also be an error, since the value is coming from developer code which is precisely being tested, so we should say politely that the test failed rather than tracking back.
|
Thanks for the branch, Zygmunt. Looks great. There are a number of comments, but they are pretty much entirely about trivial details rather than something relevant. Feel free to merge this if you feel you've addressed the comments appropriately. If you'd like to raise something, feel free to poke me online. |
|
That is, assuming you get the +1 from someone else as well. I thought you did, but looking above I can't find any. |
niemeyer
reviewed
Nov 10, 2015
| +// Contains is a Checker that looks for a needle in a haystack. | ||
| +// The needle can be any object. The haystack can be an array, slice or string. | ||
| +var Contains check.Checker = &containsChecker{ | ||
| + &check.CheckerInfo{Name: "Contains", Params: []string{"haystack", "needle"}}, |
niemeyer
referenced this pull request
Nov 10, 2015
Closed
Add the testutil package with the Contains Checker #68
|
LGTM. |
zyga commentedNov 9, 2015
This patch adds a trivial package for snappy capabilities. So far
there's just a set of basic types Capability, CapabilityType, one
function NewCapability and some tests.
Signed-off-by: Zygmunt Krynicki zygmunt.krynicki@canonical.com