From 1862ec6b6f28a17b3b6dc1326accf76eed574ba7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 26 Jul 2016 13:53:51 -0700 Subject: [PATCH 01/20] Godeps: Roll runtime-spec back to v1.0.0-rc1 The commit reference that landed in 2d0dbcf0 (Update runtime-spec dependency to v1.0.0-rc1, 2016-06-14, #110) was a few commits after the tag, but the v1.0.0.rc1 branch is aiming for compatibility with the tagged release (e.g. no -dev suffix on the generated version). Signed-off-by: W. Trevor King --- Godeps/Godeps.json | 4 ++-- .../opencontainers/runtime-spec/specs-go/config.go | 8 ++++---- .../opencontainers/runtime-spec/specs-go/version.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1ae1322a5..947f5121d 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -15,8 +15,8 @@ }, { "ImportPath": "github.com/opencontainers/runtime-spec/specs-go", - "Comment": "v1.0.0-rc1-15-g6de52a7", - "Rev": "6de52a7d39c52a1e287182d0b4e6c03068236639" + "Comment": "v1.0.0-rc1", + "Rev": "06479209bdc0d4135911688c18157bd39bd99c22" }, { "ImportPath": "github.com/syndtr/gocapability/capability", diff --git a/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/config.go b/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/config.go index 015e032d0..ec99035bb 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -53,15 +53,15 @@ type Process struct { SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"` } -// User specifies Linux/Solaris specific user and group information for the container's +// User specifies Linux specific user and group information for the container's // main process. type User struct { // UID is the user id. (this field is platform dependent) - UID uint32 `json:"uid" platform:"linux,solaris"` + UID uint32 `json:"uid" platform:"linux"` // GID is the group id. (this field is platform dependent) - GID uint32 `json:"gid" platform:"linux,solaris"` + GID uint32 `json:"gid" platform:"linux"` // AdditionalGids are additional group ids set for the container's process. (this field is platform dependent) - AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"` + AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux"` } // Root contains information about the container's root filesystem on the host. diff --git a/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/version.go b/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/version.go index 2db1b8018..2dbd302e3 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/version.go +++ b/Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -11,7 +11,7 @@ const ( VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "-rc1-dev" + VersionDev = "-rc1" ) // Version is the specification version that the package types support. From 4aa007180ea09ad14c1d41982c7f57f1779fc630 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 27 Jul 2016 21:41:29 -0700 Subject: [PATCH 02/20] generate: Remove superfluous err check from Save Signed-off-by: W. Trevor King --- generate/generate.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/generate/generate.go b/generate/generate.go index 8cf98179e..5b60e6f47 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -187,11 +187,7 @@ func (g Generator) Save(w io.Writer) error { } _, err = w.Write(data) - if err != nil { - return err - } - - return nil + return err } // SaveToFile writes the spec into a file. From bbe46e2a42c082cfd5e71b9d61412a38d7316f99 Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Sun, 24 Jul 2016 16:12:12 -0400 Subject: [PATCH 03/20] Check pointer fields of g.spec Signed-off-by: Haiyan Meng Backported to v1.0.0-rc1 and removed the references to a Linux pointer field. Signed-off-by: W. Trevor King --- generate/generate.go | 125 +++++++++++++++++++++++++++++++------------ generate/spec.go | 57 ++++++++++++++++++++ 2 files changed, 148 insertions(+), 34 deletions(-) create mode 100644 generate/spec.go diff --git a/generate/generate.go b/generate/generate.go index e189d3c66..b10abab1c 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -206,34 +206,37 @@ func (g *Generator) SaveToFile(path string) error { // SetVersion sets g.spec.Version. func (g *Generator) SetVersion(version string) { + g.initSpec() g.spec.Version = version } // SetRootPath sets g.spec.Root.Path. func (g *Generator) SetRootPath(path string) { + g.initSpec() g.spec.Root.Path = path } // SetRootReadonly sets g.spec.Root.Readonly. func (g *Generator) SetRootReadonly(b bool) { + g.initSpec() g.spec.Root.Readonly = b } // SetHostname sets g.spec.Hostname. func (g *Generator) SetHostname(s string) { + g.initSpec() g.spec.Hostname = s } // ClearAnnotations clears g.spec.Annotations. func (g *Generator) ClearAnnotations() { + g.initSpec() g.spec.Annotations = make(map[string]string) } // AddAnnotation adds an annotation into g.spec.Annotations. func (g *Generator) AddAnnotation(s string) error { - if g.spec.Annotations == nil { - g.spec.Annotations = make(map[string]string) - } + g.initSpecAnnotations() pair := strings.Split(s, "=") if len(pair) != 2 { @@ -245,7 +248,7 @@ func (g *Generator) AddAnnotation(s string) error { // RemoveAnnotation remove an annotation from g.spec.Annotations. func (g *Generator) RemoveAnnotation(key string) { - if g.spec.Annotations == nil { + if g.spec == nil || g.spec.Annotations == nil { return } delete(g.spec.Annotations, key) @@ -253,61 +256,73 @@ func (g *Generator) RemoveAnnotation(key string) { // SetPlatformOS sets g.spec.Process.OS. func (g *Generator) SetPlatformOS(os string) { + g.initSpec() g.spec.Platform.OS = os } // SetPlatformArch sets g.spec.Platform.Arch. func (g *Generator) SetPlatformArch(arch string) { + g.initSpec() g.spec.Platform.Arch = arch } // SetProcessUID sets g.spec.Process.User.UID. func (g *Generator) SetProcessUID(uid uint32) { + g.initSpec() g.spec.Process.User.UID = uid } // SetProcessGID sets g.spec.Process.User.GID. func (g *Generator) SetProcessGID(gid uint32) { + g.initSpec() g.spec.Process.User.GID = gid } // SetProcessCwd sets g.spec.Process.Cwd. func (g *Generator) SetProcessCwd(cwd string) { + g.initSpec() g.spec.Process.Cwd = cwd } // SetProcessNoNewPrivileges sets g.spec.Process.NoNewPrivileges. func (g *Generator) SetProcessNoNewPrivileges(b bool) { + g.initSpec() g.spec.Process.NoNewPrivileges = b } // SetProcessTerminal sets g.spec.Process.Terminal. func (g *Generator) SetProcessTerminal(b bool) { + g.initSpec() g.spec.Process.Terminal = b } // SetProcessApparmorProfile sets g.spec.Process.ApparmorProfile. func (g *Generator) SetProcessApparmorProfile(prof string) { + g.initSpec() g.spec.Process.ApparmorProfile = prof } // SetProcessArgs sets g.spec.Process.Args. func (g *Generator) SetProcessArgs(args []string) { + g.initSpec() g.spec.Process.Args = args } // ClearProcessEnv clears g.spec.Process.Env. func (g *Generator) ClearProcessEnv() { + g.initSpec() g.spec.Process.Env = []string{} } // AddProcessEnv adds env into g.spec.Process.Env. func (g *Generator) AddProcessEnv(env string) { + g.initSpec() g.spec.Process.Env = append(g.spec.Process.Env, env) } // ClearProcessAdditionalGids clear g.spec.Process.AdditionalGids. func (g *Generator) ClearProcessAdditionalGids() { + g.initSpec() g.spec.Process.User.AdditionalGids = []uint32{} } @@ -318,6 +333,7 @@ func (g *Generator) AddProcessAdditionalGid(gid string) error { return err } + g.initSpec() for _, group := range g.spec.Process.User.AdditionalGids { if group == uint32(groupID) { return nil @@ -329,94 +345,111 @@ func (g *Generator) AddProcessAdditionalGid(gid string) error { // SetProcessSelinuxLabel sets g.spec.Process.SelinuxLabel. func (g *Generator) SetProcessSelinuxLabel(label string) { + g.initSpec() g.spec.Process.SelinuxLabel = label } // SetLinuxCgroupsPath sets g.spec.Linux.CgroupsPath. func (g *Generator) SetLinuxCgroupsPath(path string) { + g.initSpecLinux() g.spec.Linux.CgroupsPath = strPtr(path) } // SetLinuxMountLabel sets g.spec.Linux.MountLabel. func (g *Generator) SetLinuxMountLabel(label string) { + g.initSpecLinux() g.spec.Linux.MountLabel = label } // SetLinuxResourcesCPUShares sets g.spec.Linux.Resources.CPU.Shares. func (g *Generator) SetLinuxResourcesCPUShares(shares uint64) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.Shares = &shares } // SetLinuxResourcesCPUQuota sets g.spec.Linux.Resources.CPU.Quota. func (g *Generator) SetLinuxResourcesCPUQuota(quota uint64) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.Quota = "a } // SetLinuxResourcesCPUPeriod sets g.spec.Linux.Resources.CPU.Period. func (g *Generator) SetLinuxResourcesCPUPeriod(period uint64) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.Period = &period } // SetLinuxResourcesCPURealtimeRuntime sets g.spec.Linux.Resources.CPU.RealtimeRuntime. func (g *Generator) SetLinuxResourcesCPURealtimeRuntime(time uint64) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.RealtimeRuntime = &time } // SetLinuxResourcesCPURealtimePeriod sets g.spec.Linux.Resources.CPU.RealtimePeriod. func (g *Generator) SetLinuxResourcesCPURealtimePeriod(period uint64) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.RealtimePeriod = &period } // SetLinuxResourcesCPUCpus sets g.spec.Linux.Resources.CPU.Cpus. func (g *Generator) SetLinuxResourcesCPUCpus(cpus string) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.Cpus = &cpus } // SetLinuxResourcesCPUMems sets g.spec.Linux.Resources.CPU.Mems. func (g *Generator) SetLinuxResourcesCPUMems(mems string) { + g.initSpecLinuxResourcesCPU() g.spec.Linux.Resources.CPU.Mems = &mems } // SetLinuxResourcesMemoryLimit sets g.spec.Linux.Resources.Memory.Limit. func (g *Generator) SetLinuxResourcesMemoryLimit(limit uint64) { + g.initSpecLinuxResourcesMemory() g.spec.Linux.Resources.Memory.Limit = &limit } // SetLinuxResourcesMemoryReservation sets g.spec.Linux.Resources.Memory.Reservation. func (g *Generator) SetLinuxResourcesMemoryReservation(reservation uint64) { + g.initSpecLinuxResourcesMemory() g.spec.Linux.Resources.Memory.Reservation = &reservation } // SetLinuxResourcesMemorySwap sets g.spec.Linux.Resources.Memory.Swap. func (g *Generator) SetLinuxResourcesMemorySwap(swap uint64) { + g.initSpecLinuxResourcesMemory() g.spec.Linux.Resources.Memory.Swap = &swap } // SetLinuxResourcesMemoryKernel sets g.spec.Linux.Resources.Memory.Kernel. func (g *Generator) SetLinuxResourcesMemoryKernel(kernel uint64) { + g.initSpecLinuxResourcesMemory() g.spec.Linux.Resources.Memory.Kernel = &kernel } // SetLinuxResourcesMemoryKernelTCP sets g.spec.Linux.Resources.Memory.KernelTCP. func (g *Generator) SetLinuxResourcesMemoryKernelTCP(kernelTCP uint64) { + g.initSpecLinuxResourcesMemory() g.spec.Linux.Resources.Memory.KernelTCP = &kernelTCP } // SetLinuxResourcesMemorySwappiness sets g.spec.Linux.Resources.Memory.Swappiness. func (g *Generator) SetLinuxResourcesMemorySwappiness(swappiness uint64) { + g.initSpecLinuxResourcesMemory() g.spec.Linux.Resources.Memory.Swappiness = &swappiness } // ClearLinuxSysctl clears g.spec.Linux.Sysctl. func (g *Generator) ClearLinuxSysctl() { + if g.spec == nil { + return + } g.spec.Linux.Sysctl = make(map[string]string) } // AddLinuxSysctl adds a new sysctl config into g.spec.Linux.Sysctl. func (g *Generator) AddLinuxSysctl(s string) error { - if g.spec.Linux.Sysctl == nil { - g.spec.Linux.Sysctl = make(map[string]string) - } + g.initSpecLinuxSysctl() pair := strings.Split(s, "=") if len(pair) != 2 { @@ -428,7 +461,7 @@ func (g *Generator) AddLinuxSysctl(s string) error { // RemoveLinuxSysctl removes a sysctl config from g.spec.Linux.Sysctl. func (g *Generator) RemoveLinuxSysctl(key string) { - if g.spec.Linux.Sysctl == nil { + if g.spec == nil || g.spec.Linux.Sysctl == nil { return } delete(g.spec.Linux.Sysctl, key) @@ -449,10 +482,7 @@ func (g *Generator) SetLinuxSeccompDefault(sdefault string) error { "SCMP_ACT_ALLOW") } - if g.spec.Linux.Seccomp == nil { - g.spec.Linux.Seccomp = &rspec.Seccomp{} - } - + g.initSpecLinuxSeccomp() g.spec.Linux.Seccomp.DefaultAction = rspec.Action(sdefault) return nil } @@ -483,7 +513,7 @@ func checkSeccompArch(arch string) error { // ClearLinuxSeccompArch clears g.spec.Linux.Seccomp.Architectures. func (g *Generator) ClearLinuxSeccompArch() { - if g.spec.Linux.Seccomp == nil { + if g.spec == nil || g.spec.Linux.Seccomp == nil { return } @@ -496,10 +526,7 @@ func (g *Generator) AddLinuxSeccompArch(sArch string) error { return err } - if g.spec.Linux.Seccomp == nil { - g.spec.Linux.Seccomp = &rspec.Seccomp{} - } - + g.initSpecLinuxSeccomp() g.spec.Linux.Seccomp.Architectures = append(g.spec.Linux.Seccomp.Architectures, rspec.Arch(sArch)) return nil @@ -511,7 +538,7 @@ func (g *Generator) RemoveSeccompArch(sArch string) error { return err } - if g.spec.Linux.Seccomp == nil { + if g.spec == nil || g.spec.Linux.Seccomp == nil { return nil } @@ -611,7 +638,7 @@ func parseSeccompSyscall(s string) (rspec.Syscall, error) { // ClearLinuxSeccompSyscall clears g.spec.Linux.Seccomp.Syscalls. func (g *Generator) ClearLinuxSeccompSyscall() { - if g.spec.Linux.Seccomp == nil { + if g.spec == nil || g.spec.Linux.Seccomp == nil { return } @@ -625,44 +652,37 @@ func (g *Generator) AddLinuxSeccompSyscall(sSyscall string) error { return err } - if g.spec.Linux.Seccomp == nil { - g.spec.Linux.Seccomp = &rspec.Seccomp{} - } - + g.initSpecLinuxSeccomp() g.spec.Linux.Seccomp.Syscalls = append(g.spec.Linux.Seccomp.Syscalls, f) return nil } // AddLinuxSeccompSyscallAllow adds seccompAllow into g.spec.Linux.Seccomp.Syscalls. func (g *Generator) AddLinuxSeccompSyscallAllow(seccompAllow string) { - if g.spec.Linux.Seccomp == nil { - g.spec.Linux.Seccomp = &rspec.Seccomp{} - } - syscall := rspec.Syscall{ Name: seccompAllow, Action: "SCMP_ACT_ALLOW", } + + g.initSpecLinuxSeccomp() g.spec.Linux.Seccomp.Syscalls = append(g.spec.Linux.Seccomp.Syscalls, syscall) } // AddLinuxSeccompSyscallErrno adds seccompErrno into g.spec.Linux.Seccomp.Syscalls. func (g *Generator) AddLinuxSeccompSyscallErrno(seccompErrno string) { - if g.spec.Linux.Seccomp == nil { - g.spec.Linux.Seccomp = &rspec.Seccomp{} - } - syscall := rspec.Syscall{ Name: seccompErrno, Action: "SCMP_ACT_ERRNO", } + + g.initSpecLinuxSeccomp() g.spec.Linux.Seccomp.Syscalls = append(g.spec.Linux.Seccomp.Syscalls, syscall) } // RemoveSeccompSyscallByName removes all the seccomp syscalls with the given // name from g.spec.Linux.Seccomp.Syscalls. func (g *Generator) RemoveSeccompSyscallByName(name string) error { - if g.spec.Linux.Seccomp == nil { + if g.spec == nil || g.spec.Linux.Seccomp == nil { return nil } @@ -679,7 +699,7 @@ func (g *Generator) RemoveSeccompSyscallByName(name string) error { // RemoveSeccompSyscallByAction removes all the seccomp syscalls with the given // action from g.spec.Linux.Seccomp.Syscalls. func (g *Generator) RemoveSeccompSyscallByAction(action string) error { - if g.spec.Linux.Seccomp == nil { + if g.spec == nil || g.spec.Linux.Seccomp == nil { return nil } @@ -700,7 +720,7 @@ func (g *Generator) RemoveSeccompSyscallByAction(action string) error { // RemoveSeccompSyscall removes all the seccomp syscalls with the given // name and action from g.spec.Linux.Seccomp.Syscalls. func (g *Generator) RemoveSeccompSyscall(name string, action string) error { - if g.spec.Linux.Seccomp == nil { + if g.spec == nil || g.spec.Linux.Seccomp == nil { return nil } @@ -750,6 +770,9 @@ func parseIDMapping(idms string) (rspec.IDMapping, error) { // ClearLinuxUIDMappings clear g.spec.Linux.UIDMappings. func (g *Generator) ClearLinuxUIDMappings() { + if g.spec == nil { + return + } g.spec.Linux.UIDMappings = []rspec.IDMapping{} } @@ -760,12 +783,16 @@ func (g *Generator) AddLinuxUIDMapping(uidMap string) error { return err } + g.initSpecLinux() g.spec.Linux.UIDMappings = append(g.spec.Linux.UIDMappings, r) return nil } // ClearLinuxGIDMappings clear g.spec.Linux.GIDMappings. func (g *Generator) ClearLinuxGIDMappings() { + if g.spec == nil { + return + } g.spec.Linux.GIDMappings = []rspec.IDMapping{} } @@ -776,6 +803,7 @@ func (g *Generator) AddLinuxGIDMapping(gidMap string) error { return err } + g.initSpecLinux() g.spec.Linux.GIDMappings = append(g.spec.Linux.GIDMappings, r) return nil } @@ -793,6 +821,7 @@ func (g *Generator) SetLinuxRootPropagation(rp string) error { default: return fmt.Errorf("rootfs-propagation must be empty or one of private|rprivate|slave|rslave|shared|rshared") } + g.initSpecLinux() g.spec.Linux.RootfsPropagation = rp return nil } @@ -809,36 +838,48 @@ func parseHook(s string) rspec.Hook { // ClearPreStartHooks clear g.spec.Hooks.Prestart. func (g *Generator) ClearPreStartHooks() { + if g.spec == nil { + return + } g.spec.Hooks.Prestart = []rspec.Hook{} } // AddPreStartHook add a prestart hook into g.spec.Hooks.Prestart. func (g *Generator) AddPreStartHook(s string) error { hook := parseHook(s) + g.initSpec() g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook) return nil } // ClearPostStopHooks clear g.spec.Hooks.Poststop. func (g *Generator) ClearPostStopHooks() { + if g.spec == nil { + return + } g.spec.Hooks.Poststop = []rspec.Hook{} } // AddPostStopHook adds a poststop hook into g.spec.Hooks.Poststop. func (g *Generator) AddPostStopHook(s string) error { hook := parseHook(s) + g.initSpec() g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook) return nil } // ClearPostStartHooks clear g.spec.Hooks.Poststart. func (g *Generator) ClearPostStartHooks() { + if g.spec == nil { + return + } g.spec.Hooks.Poststart = []rspec.Hook{} } // AddPostStartHook adds a poststart hook into g.spec.Hooks.Poststart. func (g *Generator) AddPostStartHook(s string) error { hook := parseHook(s) + g.initSpec() g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook) return nil } @@ -852,6 +893,7 @@ func (g *Generator) AddTmpfsMount(dest string) error { Options: []string{"nosuid", "nodev", "mode=755"}, } + g.initSpec() g.spec.Mounts = append(g.spec.Mounts, mnt) return nil } @@ -873,6 +915,7 @@ func (g *Generator) AddCgroupsMount(mountCgroupOption string) error { Source: "cgroup", Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption}, } + g.initSpec() g.spec.Mounts = append(g.spec.Mounts, mnt) return nil @@ -899,6 +942,7 @@ func (g *Generator) AddBindMount(bind string) error { Source: source, Options: append(defaultOptions, options), } + g.initSpec() g.spec.Mounts = append(g.spec.Mounts, mnt) return nil } @@ -911,6 +955,7 @@ func (g *Generator) SetupPrivileged(privileged bool) { for _, cap := range capability.List() { finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))) } + g.initSpecLinux() g.spec.Process.Capabilities = finalCapList g.spec.Process.SelinuxLabel = "" g.spec.Process.ApparmorProfile = "" @@ -937,6 +982,9 @@ func checkCap(c string) error { // ClearProcessCapabilities clear g.spec.Process.Capabilities. func (g *Generator) ClearProcessCapabilities() { + if g.spec == nil { + return + } g.spec.Process.Capabilities = []string{} } @@ -948,6 +996,7 @@ func (g *Generator) AddProcessCapability(c string) error { cp := fmt.Sprintf("CAP_%s", strings.ToUpper(c)) + g.initSpec() for _, cap := range g.spec.Process.Capabilities { if strings.ToUpper(cap) == cp { return nil @@ -966,6 +1015,7 @@ func (g *Generator) DropProcessCapability(c string) error { cp := fmt.Sprintf("CAP_%s", strings.ToUpper(c)) + g.initSpec() for i, cap := range g.spec.Process.Capabilities { if strings.ToUpper(cap) == cp { g.spec.Process.Capabilities = append(g.spec.Process.Capabilities[:i], g.spec.Process.Capabilities[i+1:]...) @@ -999,6 +1049,9 @@ func mapStrToNamespace(ns string, path string) (rspec.Namespace, error) { // ClearLinuxNamespaces clear g.spec.Linux.Namespaces. func (g *Generator) ClearLinuxNamespaces() { + if g.spec == nil { + return + } g.spec.Linux.Namespaces = []rspec.Namespace{} } @@ -1010,6 +1063,7 @@ func (g *Generator) AddOrReplaceLinuxNamespace(ns string, path string) error { return err } + g.initSpecLinux() for i, ns := range g.spec.Linux.Namespaces { if ns.Type == namespace.Type { g.spec.Linux.Namespaces[i] = namespace @@ -1027,6 +1081,9 @@ func (g *Generator) RemoveLinuxNamespace(ns string) error { return err } + if g.spec == nil { + return nil + } for i, ns := range g.spec.Linux.Namespaces { if ns.Type == namespace.Type { g.spec.Linux.Namespaces = append(g.spec.Linux.Namespaces[:i], g.spec.Linux.Namespaces[i+1:]...) diff --git a/generate/spec.go b/generate/spec.go new file mode 100644 index 000000000..571443a8a --- /dev/null +++ b/generate/spec.go @@ -0,0 +1,57 @@ +package generate + +import ( + rspec "github.com/opencontainers/runtime-spec/specs-go" +) + +func (g *Generator) initSpec() { + if g.spec == nil { + g.spec = &rspec.Spec{} + } +} + +func (g *Generator) initSpecAnnotations() { + g.initSpec() + if g.spec.Annotations == nil { + g.spec.Annotations = make(map[string]string) + } +} + +func (g *Generator) initSpecLinux() { + g.initSpec() +} + +func (g *Generator) initSpecLinuxSysctl() { + g.initSpecLinux() + if g.spec.Linux.Sysctl == nil { + g.spec.Linux.Sysctl = make(map[string]string) + } +} + +func (g *Generator) initSpecLinuxSeccomp() { + g.initSpecLinux() + if g.spec.Linux.Seccomp == nil { + g.spec.Linux.Seccomp = &rspec.Seccomp{} + } +} + +func (g *Generator) initSpecLinuxResources() { + g.initSpecLinux() + if g.spec.Linux.Resources == nil { + g.spec.Linux.Resources = &rspec.Resources{} + } +} + +func (g *Generator) initSpecLinuxResourcesCPU() { + g.initSpecLinuxResources() + if g.spec.Linux.Resources.CPU == nil { + g.spec.Linux.Resources.CPU = &rspec.CPU{} + } +} + +func (g *Generator) initSpecLinuxResourcesMemory() { + g.initSpecLinuxResources() + if g.spec.Linux.Resources.Memory == nil { + g.spec.Linux.Resources.Memory = &rspec.Memory{} + } +} From 5295830ea769a6b984a7c92e5d75c83b644ee699 Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Thu, 21 Jul 2016 12:30:18 -0400 Subject: [PATCH 04/20] Check CAP_LAST_CAP while setting privileged Signed-off-by: Haiyan Meng Backported to v1.0.0-rc1 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 12 ++++++++---- cmd/ocitools/main.go | 4 ++++ cmd/ocitools/validate.go | 3 +-- generate/generate.go | 31 ++++++++++++++++++++++--------- man/ocitools-validate.1.md | 10 ---------- man/ocitools.1.md | 17 ++++++++++++++++- 6 files changed, 51 insertions(+), 26 deletions(-) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index fca5d6fcb..a6423a028 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -78,7 +78,7 @@ var generateCommand = cli.Command{ } } - err := setupSpec(specgen, context) + err := setupSpec(&specgen, context) if err != nil { return err } @@ -96,8 +96,12 @@ var generateCommand = cli.Command{ }, } -func setupSpec(g generate.Generator, context *cli.Context) error { - spec := g.GetSpec() +func setupSpec(g *generate.Generator, context *cli.Context) error { + if context.GlobalBool("host-specific") { + g.HostSpecific = true + } + + spec := g.Spec() if len(spec.Version) == 0 { g.SetVersion(rspec.Version) @@ -369,7 +373,7 @@ func checkNs(nsMaps map[string]string, nsName string) bool { return true } -func setupLinuxNamespaces(g generate.Generator, needsNewUser bool, nsMaps map[string]string) { +func setupLinuxNamespaces(g *generate.Generator, needsNewUser bool, nsMaps map[string]string) { for _, nsName := range generate.Namespaces { if !checkNs(nsMaps, nsName) && !(needsNewUser && nsName == "user") { continue diff --git a/cmd/ocitools/main.go b/cmd/ocitools/main.go index daf8d4359..7ced60de2 100644 --- a/cmd/ocitools/main.go +++ b/cmd/ocitools/main.go @@ -18,6 +18,10 @@ func main() { Value: "error", Usage: "Log level (panic, fatal, error, warn, info, or debug)", }, + cli.BoolFlag{ + Name: "host-specific", + Usage: "generate host-specific configs or do host-specific validations", + }, } app.Commands = []cli.Command{ diff --git a/cmd/ocitools/validate.go b/cmd/ocitools/validate.go index 760b44ea4..0fab50202 100644 --- a/cmd/ocitools/validate.go +++ b/cmd/ocitools/validate.go @@ -24,7 +24,6 @@ type configCheck func(rspec.Spec, string, bool) []string var bundleValidateFlags = []cli.Flag{ cli.StringFlag{Name: "path", Value: ".", Usage: "path to a bundle"}, - cli.BoolFlag{Name: "host-specific", Usage: "Check host specific configs."}, } var ( @@ -99,7 +98,7 @@ var bundleValidateCommand = cli.Command{ return fmt.Errorf("The root path %q is not a directory.", rootfsPath) } - hostCheck := context.Bool("host-specific") + hostCheck := context.GlobalBool("host-specific") checks := []configCheck{ checkMandatoryFields, diff --git a/generate/generate.go b/generate/generate.go index e189d3c66..f6f9b3c18 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -21,7 +21,8 @@ var ( // Generator represents a generator for a container spec. type Generator struct { - spec *rspec.Spec + spec *rspec.Spec + HostSpecific bool } // New creates a spec Generator with the default spec. @@ -139,12 +140,16 @@ func New() Generator { Devices: []rspec.Device{}, }, } - return Generator{&spec} + return Generator{ + spec: &spec, + } } // NewFromSpec creates a spec Generator from a given spec. func NewFromSpec(spec *rspec.Spec) Generator { - return Generator{spec} + return Generator{ + spec: spec, + } } // NewFromFile loads the template specifed in a file into a spec Generator. @@ -166,7 +171,9 @@ func NewFromTemplate(r io.Reader) (Generator, error) { if err := json.NewDecoder(r).Decode(&spec); err != nil { return Generator{}, err } - return Generator{&spec}, nil + return Generator{ + spec: &spec, + }, nil } // SetSpec sets the spec in the Generator g. @@ -174,8 +181,8 @@ func (g *Generator) SetSpec(spec *rspec.Spec) { g.spec = spec } -// GetSpec gets the spec in the Generator g. -func (g *Generator) GetSpec() *rspec.Spec { +// Spec gets the spec in the Generator g. +func (g *Generator) Spec() *rspec.Spec { return g.spec } @@ -909,6 +916,9 @@ func (g *Generator) SetupPrivileged(privileged bool) { // Add all capabilities in privileged mode. var finalCapList []string for _, cap := range capability.List() { + if g.HostSpecific && cap > capability.CAP_LAST_CAP { + continue + } finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))) } g.spec.Process.Capabilities = finalCapList @@ -918,12 +928,15 @@ func (g *Generator) SetupPrivileged(privileged bool) { } } -func checkCap(c string) error { +func checkCap(c string, hostSpecific bool) error { isValid := false cp := strings.ToUpper(c) for _, cap := range capability.List() { if cp == strings.ToUpper(cap.String()) { + if hostSpecific && cap > capability.CAP_LAST_CAP { + return fmt.Errorf("CAP_%s is not supported on the current host", cp) + } isValid = true break } @@ -942,7 +955,7 @@ func (g *Generator) ClearProcessCapabilities() { // AddProcessCapability adds a process capability into g.spec.Process.Capabilities. func (g *Generator) AddProcessCapability(c string) error { - if err := checkCap(c); err != nil { + if err := checkCap(c, g.HostSpecific); err != nil { return err } @@ -960,7 +973,7 @@ func (g *Generator) AddProcessCapability(c string) error { // DropProcessCapability drops a process capability from g.spec.Process.Capabilities. func (g *Generator) DropProcessCapability(c string) error { - if err := checkCap(c); err != nil { + if err := checkCap(c, g.HostSpecific); err != nil { return err } diff --git a/man/ocitools-validate.1.md b/man/ocitools-validate.1.md index a65a152cc..e5e3a1b8b 100644 --- a/man/ocitools-validate.1.md +++ b/man/ocitools-validate.1.md @@ -18,16 +18,6 @@ Validate an OCI bundle **--path=PATH Path to bundle -**--host-specific** - Check host specific configs. - By default, validation only tests for compatibility with a hypothetical host. - With this flag, validation will also run more specific tests to see whether - the current host is capable of launching a container from the configuration. - For example, validating a compliant Windows configuration on a Linux machine - will pass without this flag ("there may be a Windows host capable of - launching this container"), but will fail with it ("this host is not capable - of launching this container"). - # SEE ALSO **ocitools**(1) diff --git a/man/ocitools.1.md b/man/ocitools.1.md index bf338b2ab..a698fff4e 100644 --- a/man/ocitools.1.md +++ b/man/ocitools.1.md @@ -15,11 +15,26 @@ ocitools is a collection of tools for working with the [OCI runtime specificatio # OPTIONS **--help** - Print usage statement + Print usage statement. **-v**, **--version** Print version information. +**--log-level** + Log level (panic, fatal, error, warn, info, or debug) (default: "error"). + +**--host-specific** + Generate host-specific configs or do host-specific validations. + + By default, generator generates configs without checking whether they are + supported on the current host. With this flag, generator will first check + whether each config is supported on the current host, and only add it into + the config file if it passes the checking. + + By default, validation only tests for compatibility with a hypothetical host. + With this flag, validation will also run more specific tests to see whether + the current host is capable of launching a container from the configuration. + # COMMANDS **validate** Validating OCI bundle From 3968a06eb3c1e2583d0efd80a7756cc6f343a1f0 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Wed, 27 Jul 2016 17:38:50 +0800 Subject: [PATCH 05/20] generate: fix tmpfs adding based on manpage Signed-off-by: Ma Shimiao Backported to v1.0.0-rc1 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- generate/generate.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/generate/generate.go b/generate/generate.go index e189d3c66..4bb9c15d1 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -845,11 +845,26 @@ func (g *Generator) AddPostStartHook(s string) error { // AddTmpfsMount adds a tmpfs mount into g.spec.Mounts. func (g *Generator) AddTmpfsMount(dest string) error { - mnt := rspec.Mount{ - Destination: dest, - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "nodev", "mode=755"}, + mnt := rspec.Mount{} + + parts := strings.Split(dest, ":") + if len(parts) == 2 { + options := strings.Split(parts[1], ",") + mnt = rspec.Mount{ + Destination: parts[0], + Type: "tmpfs", + Source: "tmpfs", + Options: options, + } + } else if len(parts) == 1 { + mnt = rspec.Mount{ + Destination: parts[0], + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}, + } + } else { + return fmt.Errorf("invalid value for --tmpfs") } g.spec.Mounts = append(g.spec.Mounts, mnt) From 928bf892208e41b8135000a0b4f93664f217ccad Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Thu, 28 Jul 2016 23:51:38 +0800 Subject: [PATCH 06/20] generate: remove unnecessary spec initialization Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from 1371b52 #168 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- generate/generate.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/generate/generate.go b/generate/generate.go index 851571ed8..ddaa644c4 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -237,7 +237,9 @@ func (g *Generator) SetHostname(s string) { // ClearAnnotations clears g.spec.Annotations. func (g *Generator) ClearAnnotations() { - g.initSpec() + if g.spec == nil { + return + } g.spec.Annotations = make(map[string]string) } @@ -317,7 +319,9 @@ func (g *Generator) SetProcessArgs(args []string) { // ClearProcessEnv clears g.spec.Process.Env. func (g *Generator) ClearProcessEnv() { - g.initSpec() + if g.spec == nil { + return + } g.spec.Process.Env = []string{} } @@ -329,7 +333,9 @@ func (g *Generator) AddProcessEnv(env string) { // ClearProcessAdditionalGids clear g.spec.Process.AdditionalGids. func (g *Generator) ClearProcessAdditionalGids() { - g.initSpec() + if g.spec == nil { + return + } g.spec.Process.User.AdditionalGids = []uint32{} } From e5bcb77905d40135c21da8a852c0b0b730297eed Mon Sep 17 00:00:00 2001 From: masm Date: Wed, 27 Jul 2016 23:02:59 +0800 Subject: [PATCH 07/20] generate: fix capability.List() for cap_last_cap not exist Signed-off-by: masm Backported to v1.0.0.rc1 from f7df9c7 #157 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- generate/generate.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/generate/generate.go b/generate/generate.go index 851571ed8..f969c6f9a 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -975,7 +975,7 @@ func (g *Generator) SetupPrivileged(privileged bool) { // Add all capabilities in privileged mode. var finalCapList []string for _, cap := range capability.List() { - if g.HostSpecific && cap > capability.CAP_LAST_CAP { + if g.HostSpecific && cap > lastCap() { continue } finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))) @@ -988,13 +988,23 @@ func (g *Generator) SetupPrivileged(privileged bool) { } } +func lastCap() capability.Cap { + last := capability.CAP_LAST_CAP + // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap + if last == capability.Cap(63) { + last = capability.CAP_BLOCK_SUSPEND + } + + return last +} + func checkCap(c string, hostSpecific bool) error { isValid := false cp := strings.ToUpper(c) for _, cap := range capability.List() { if cp == strings.ToUpper(cap.String()) { - if hostSpecific && cap > capability.CAP_LAST_CAP { + if hostSpecific && cap > lastCap() { return fmt.Errorf("CAP_%s is not supported on the current host", cp) } isValid = true From 11f1b121ec60e1c6b88922b4f2463893fbdbb08f Mon Sep 17 00:00:00 2001 From: Chris Aniszczyk Date: Thu, 28 Jul 2016 18:55:36 -0500 Subject: [PATCH 08/20] Add Travis CI badge to README https://travis-ci.org/opencontainers/ocitools Signed-off-by: Chris Aniszczyk Backported to v1.0.0.rc1 from d0ad8ca #170 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0a5e1678..b06dbb3e4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ocitools +# ocitools [![Build Status](https://travis-ci.org/opencontainers/ocitools.svg?branch=master)](https://travis-ci.org/opencontainers/ocitools) ocitools is a collection of tools for working with the [OCI runtime specification][runtime-spec]. From f43eacf93782da86c319a0c7115578719f4e9804 Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Thu, 28 Jul 2016 17:37:23 -0400 Subject: [PATCH 09/20] Modify generate API Signed-off-by: Haiyan Meng Backported to v1.0.0.rc1 from a217d8c #169 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 121 ++++++++++++++++++++++++++++++++--- generate/generate.go | 133 ++++++++++----------------------------- 2 files changed, 144 insertions(+), 110 deletions(-) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index a6423a028..a3990dce6 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -1,8 +1,11 @@ package main import ( + "fmt" "os" "runtime" + "strconv" + "strings" "github.com/opencontainers/ocitools/generate" rspec "github.com/opencontainers/runtime-spec/specs-go" @@ -117,7 +120,11 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("label") { annotations := context.StringSlice("label") for _, s := range annotations { - if err := g.AddAnnotation(s); err != nil { + pair := strings.Split(s, "=") + if len(pair) != 2 { + return fmt.Errorf("incorrectly specified annotation: %s", s) + } + if err := g.AddAnnotation(pair[0], pair[1]); err != nil { return err } } @@ -184,7 +191,11 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("sysctl") { sysctls := context.StringSlice("sysctl") for _, s := range sysctls { - g.AddLinuxSysctl(s) + pair := strings.Split(s, "=") + if len(pair) != 2 { + return fmt.Errorf("incorrectly specified sysctl: %s", s) + } + g.AddLinuxSysctl(pair[0], pair[1]) } } @@ -239,7 +250,11 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("tmpfs") { tmpfsSlice := context.StringSlice("tmpfs") for _, s := range tmpfsSlice { - if err := g.AddTmpfsMount(s); err != nil { + dest, options, err := parseTmpfsMount(s) + if err != nil { + return err + } + if err := g.AddTmpfsMount(dest, options); err != nil { return err } } @@ -253,7 +268,12 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("bind") { binds := context.StringSlice("bind") for _, bind := range binds { - if err := g.AddBindMount(bind); err != nil { + source, dest, options, err := parseBindMount(bind) + if err != nil { + return err + } + + if err := g.AddBindMount(source, dest, options); err != nil { return err } } @@ -262,7 +282,8 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("prestart") { preStartHooks := context.StringSlice("prestart") for _, hook := range preStartHooks { - if err := g.AddPreStartHook(hook); err != nil { + path, args := parseHook(hook) + if err := g.AddPreStartHook(path, args); err != nil { return err } } @@ -271,7 +292,8 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("poststop") { postStopHooks := context.StringSlice("poststop") for _, hook := range postStopHooks { - if err := g.AddPostStopHook(hook); err != nil { + path, args := parseHook(hook) + if err := g.AddPostStopHook(path, args); err != nil { return err } } @@ -280,7 +302,8 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("poststart") { postStartHooks := context.StringSlice("poststart") for _, hook := range postStartHooks { - if err := g.AddPostStartHook(hook); err != nil { + path, args := parseHook(hook) + if err := g.AddPostStartHook(path, args); err != nil { return err } } @@ -294,13 +317,23 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { } for _, uidMap := range uidMaps { - if err := g.AddLinuxUIDMapping(uidMap); err != nil { + hid, cid, size, err := parseIDMapping(uidMap) + if err != nil { + return err + } + + if err := g.AddLinuxUIDMapping(hid, cid, size); err != nil { return err } } for _, gidMap := range gidMaps { - if err := g.AddLinuxGIDMapping(gidMap); err != nil { + hid, cid, size, err := parseIDMapping(gidMap) + if err != nil { + return err + } + + if err := g.AddLinuxGIDMapping(hid, cid, size); err != nil { return err } } @@ -386,3 +419,73 @@ func setupLinuxNamespaces(g *generate.Generator, needsNewUser bool, nsMaps map[s g.AddOrReplaceLinuxNamespace(nsName, nsPath) } } + +func parseIDMapping(idms string) (uint32, uint32, uint32, error) { + idm := strings.Split(idms, ":") + if len(idm) != 3 { + return 0, 0, 0, fmt.Errorf("idmappings error: %s", idms) + } + + hid, err := strconv.Atoi(idm[0]) + if err != nil { + return 0, 0, 0, err + } + + cid, err := strconv.Atoi(idm[1]) + if err != nil { + return 0, 0, 0, err + } + + size, err := strconv.Atoi(idm[2]) + if err != nil { + return 0, 0, 0, err + } + + return uint32(hid), uint32(cid), uint32(size), nil +} + +func parseHook(s string) (string, []string) { + parts := strings.Split(s, ":") + args := []string{} + path := parts[0] + if len(parts) > 1 { + args = parts[1:] + } + return path, args +} + +func parseTmpfsMount(s string) (string, []string, error) { + var dest string + var options []string + var err error + + parts := strings.Split(s, ":") + if len(parts) == 2 { + dest = parts[0] + options = strings.Split(parts[1], ",") + } else if len(parts) == 1 { + dest = parts[0] + options = []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"} + } else { + err = fmt.Errorf("invalid value for --tmpfs") + } + + return dest, options, err +} + +func parseBindMount(s string) (string, string, string, error) { + var source, dest string + options := "ro" + + bparts := strings.SplitN(s, ":", 3) + switch len(bparts) { + case 2: + source, dest = bparts[0], bparts[1] + case 3: + source, dest, options = bparts[0], bparts[1], bparts[2] + default: + return source, dest, options, fmt.Errorf("--bind should have format src:dest:[options]") + } + + return source, dest, options, nil +} diff --git a/generate/generate.go b/generate/generate.go index 39bc7f814..f699d1a99 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -240,14 +240,9 @@ func (g *Generator) ClearAnnotations() { } // AddAnnotation adds an annotation into g.spec.Annotations. -func (g *Generator) AddAnnotation(s string) error { +func (g *Generator) AddAnnotation(key, value string) error { g.initSpecAnnotations() - - pair := strings.Split(s, "=") - if len(pair) != 2 { - return fmt.Errorf("incorrectly specified annotation: %s", s) - } - g.spec.Annotations[pair[0]] = pair[1] + g.spec.Annotations[key] = value return nil } @@ -457,14 +452,9 @@ func (g *Generator) ClearLinuxSysctl() { } // AddLinuxSysctl adds a new sysctl config into g.spec.Linux.Sysctl. -func (g *Generator) AddLinuxSysctl(s string) error { +func (g *Generator) AddLinuxSysctl(key, value string) error { g.initSpecLinuxSysctl() - - pair := strings.Split(s, "=") - if len(pair) != 2 { - return fmt.Errorf("incorrectly specified sysctl: %s", s) - } - g.spec.Linux.Sysctl[pair[0]] = pair[1] + g.spec.Linux.Sysctl[key] = value return nil } @@ -748,35 +738,6 @@ func (g *Generator) RemoveSeccompSyscall(name string, action string) error { return nil } -func parseIDMapping(idms string) (rspec.IDMapping, error) { - idm := strings.Split(idms, ":") - if len(idm) != 3 { - return rspec.IDMapping{}, fmt.Errorf("idmappings error: %s", idms) - } - - hid, err := strconv.Atoi(idm[0]) - if err != nil { - return rspec.IDMapping{}, err - } - - cid, err := strconv.Atoi(idm[1]) - if err != nil { - return rspec.IDMapping{}, err - } - - size, err := strconv.Atoi(idm[2]) - if err != nil { - return rspec.IDMapping{}, err - } - - idMapping := rspec.IDMapping{ - HostID: uint32(hid), - ContainerID: uint32(cid), - Size: uint32(size), - } - return idMapping, nil -} - // ClearLinuxUIDMappings clear g.spec.Linux.UIDMappings. func (g *Generator) ClearLinuxUIDMappings() { if g.spec == nil { @@ -786,14 +747,15 @@ func (g *Generator) ClearLinuxUIDMappings() { } // AddLinuxUIDMapping adds uidMap into g.spec.Linux.UIDMappings. -func (g *Generator) AddLinuxUIDMapping(uidMap string) error { - r, err := parseIDMapping(uidMap) - if err != nil { - return err +func (g *Generator) AddLinuxUIDMapping(hid, cid, size uint32) error { + idMapping := rspec.IDMapping{ + HostID: hid, + ContainerID: cid, + Size: size, } g.initSpecLinux() - g.spec.Linux.UIDMappings = append(g.spec.Linux.UIDMappings, r) + g.spec.Linux.UIDMappings = append(g.spec.Linux.UIDMappings, idMapping) return nil } @@ -806,14 +768,15 @@ func (g *Generator) ClearLinuxGIDMappings() { } // AddLinuxGIDMapping adds gidMap into g.spec.Linux.GIDMappings. -func (g *Generator) AddLinuxGIDMapping(gidMap string) error { - r, err := parseIDMapping(gidMap) - if err != nil { - return err +func (g *Generator) AddLinuxGIDMapping(hid, cid, size uint32) error { + idMapping := rspec.IDMapping{ + HostID: hid, + ContainerID: cid, + Size: size, } g.initSpecLinux() - g.spec.Linux.GIDMappings = append(g.spec.Linux.GIDMappings, r) + g.spec.Linux.GIDMappings = append(g.spec.Linux.GIDMappings, idMapping) return nil } @@ -835,16 +798,6 @@ func (g *Generator) SetLinuxRootPropagation(rp string) error { return nil } -func parseHook(s string) rspec.Hook { - parts := strings.Split(s, ":") - args := []string{} - path := parts[0] - if len(parts) > 1 { - args = parts[1:] - } - return rspec.Hook{Path: path, Args: args} -} - // ClearPreStartHooks clear g.spec.Hooks.Prestart. func (g *Generator) ClearPreStartHooks() { if g.spec == nil { @@ -854,9 +807,9 @@ func (g *Generator) ClearPreStartHooks() { } // AddPreStartHook add a prestart hook into g.spec.Hooks.Prestart. -func (g *Generator) AddPreStartHook(s string) error { - hook := parseHook(s) +func (g *Generator) AddPreStartHook(path string, args []string) error { g.initSpec() + hook := rspec.Hook{Path: path, Args: args} g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook) return nil } @@ -870,9 +823,9 @@ func (g *Generator) ClearPostStopHooks() { } // AddPostStopHook adds a poststop hook into g.spec.Hooks.Poststop. -func (g *Generator) AddPostStopHook(s string) error { - hook := parseHook(s) +func (g *Generator) AddPostStopHook(path string, args []string) error { g.initSpec() + hook := rspec.Hook{Path: path, Args: args} g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook) return nil } @@ -886,35 +839,20 @@ func (g *Generator) ClearPostStartHooks() { } // AddPostStartHook adds a poststart hook into g.spec.Hooks.Poststart. -func (g *Generator) AddPostStartHook(s string) error { - hook := parseHook(s) +func (g *Generator) AddPostStartHook(path string, args []string) error { g.initSpec() + hook := rspec.Hook{Path: path, Args: args} g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook) return nil } // AddTmpfsMount adds a tmpfs mount into g.spec.Mounts. -func (g *Generator) AddTmpfsMount(dest string) error { - mnt := rspec.Mount{} - - parts := strings.Split(dest, ":") - if len(parts) == 2 { - options := strings.Split(parts[1], ",") - mnt = rspec.Mount{ - Destination: parts[0], - Type: "tmpfs", - Source: "tmpfs", - Options: options, - } - } else if len(parts) == 1 { - mnt = rspec.Mount{ - Destination: parts[0], - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}, - } - } else { - return fmt.Errorf("invalid value for --tmpfs") +func (g *Generator) AddTmpfsMount(dest string, options []string) error { + mnt := rspec.Mount{ + Destination: dest, + Type: "tmpfs", + Source: "tmpfs", + Options: options, } g.initSpec() @@ -946,20 +884,13 @@ func (g *Generator) AddCgroupsMount(mountCgroupOption string) error { } // AddBindMount adds a bind mount into g.spec.Mounts. -func (g *Generator) AddBindMount(bind string) error { - var source, dest string - options := "ro" - bparts := strings.SplitN(bind, ":", 3) - switch len(bparts) { - case 2: - source, dest = bparts[0], bparts[1] - case 3: - source, dest, options = bparts[0], bparts[1], bparts[2] - default: - return fmt.Errorf("--bind should have format src:dest:[options]") +func (g *Generator) AddBindMount(source, dest, options string) error { + if options == "" { + options = "ro" } defaultOptions := []string{"bind"} + mnt := rspec.Mount{ Destination: dest, Type: "bind", From fe62695eb893279d432504e1192fb8e2934489ff Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Fri, 29 Jul 2016 11:30:24 -0400 Subject: [PATCH 10/20] remove unnecessary return error value Signed-off-by: Haiyan Meng Backported to v1.0.0.rc1 from b31cc61 #169 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 33 ++++++++------------------------- generate/generate.go | 27 +++++++++------------------ 2 files changed, 17 insertions(+), 43 deletions(-) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index a3990dce6..39c45567c 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -124,9 +124,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if len(pair) != 2 { return fmt.Errorf("incorrectly specified annotation: %s", s) } - if err := g.AddAnnotation(pair[0], pair[1]); err != nil { - return err - } + g.AddAnnotation(pair[0], pair[1]) } } @@ -254,9 +252,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if err != nil { return err } - if err := g.AddTmpfsMount(dest, options); err != nil { - return err - } + g.AddTmpfsMount(dest, options) } } @@ -272,10 +268,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if err != nil { return err } - - if err := g.AddBindMount(source, dest, options); err != nil { - return err - } + g.AddBindMount(source, dest, options) } } @@ -283,9 +276,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { preStartHooks := context.StringSlice("prestart") for _, hook := range preStartHooks { path, args := parseHook(hook) - if err := g.AddPreStartHook(path, args); err != nil { - return err - } + g.AddPreStartHook(path, args) } } @@ -293,9 +284,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { postStopHooks := context.StringSlice("poststop") for _, hook := range postStopHooks { path, args := parseHook(hook) - if err := g.AddPostStopHook(path, args); err != nil { - return err - } + g.AddPostStopHook(path, args) } } @@ -303,9 +292,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { postStartHooks := context.StringSlice("poststart") for _, hook := range postStartHooks { path, args := parseHook(hook) - if err := g.AddPostStartHook(path, args); err != nil { - return err - } + g.AddPostStartHook(path, args) } } @@ -322,9 +309,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { return err } - if err := g.AddLinuxUIDMapping(hid, cid, size); err != nil { - return err - } + g.AddLinuxUIDMapping(hid, cid, size) } for _, gidMap := range gidMaps { @@ -333,9 +318,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { return err } - if err := g.AddLinuxGIDMapping(hid, cid, size); err != nil { - return err - } + g.AddLinuxGIDMapping(hid, cid, size) } var sd string diff --git a/generate/generate.go b/generate/generate.go index f699d1a99..dcc36a058 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -240,10 +240,9 @@ func (g *Generator) ClearAnnotations() { } // AddAnnotation adds an annotation into g.spec.Annotations. -func (g *Generator) AddAnnotation(key, value string) error { +func (g *Generator) AddAnnotation(key, value string) { g.initSpecAnnotations() g.spec.Annotations[key] = value - return nil } // RemoveAnnotation remove an annotation from g.spec.Annotations. @@ -452,10 +451,9 @@ func (g *Generator) ClearLinuxSysctl() { } // AddLinuxSysctl adds a new sysctl config into g.spec.Linux.Sysctl. -func (g *Generator) AddLinuxSysctl(key, value string) error { +func (g *Generator) AddLinuxSysctl(key, value string) { g.initSpecLinuxSysctl() g.spec.Linux.Sysctl[key] = value - return nil } // RemoveLinuxSysctl removes a sysctl config from g.spec.Linux.Sysctl. @@ -747,7 +745,7 @@ func (g *Generator) ClearLinuxUIDMappings() { } // AddLinuxUIDMapping adds uidMap into g.spec.Linux.UIDMappings. -func (g *Generator) AddLinuxUIDMapping(hid, cid, size uint32) error { +func (g *Generator) AddLinuxUIDMapping(hid, cid, size uint32) { idMapping := rspec.IDMapping{ HostID: hid, ContainerID: cid, @@ -756,7 +754,6 @@ func (g *Generator) AddLinuxUIDMapping(hid, cid, size uint32) error { g.initSpecLinux() g.spec.Linux.UIDMappings = append(g.spec.Linux.UIDMappings, idMapping) - return nil } // ClearLinuxGIDMappings clear g.spec.Linux.GIDMappings. @@ -768,7 +765,7 @@ func (g *Generator) ClearLinuxGIDMappings() { } // AddLinuxGIDMapping adds gidMap into g.spec.Linux.GIDMappings. -func (g *Generator) AddLinuxGIDMapping(hid, cid, size uint32) error { +func (g *Generator) AddLinuxGIDMapping(hid, cid, size uint32) { idMapping := rspec.IDMapping{ HostID: hid, ContainerID: cid, @@ -777,7 +774,6 @@ func (g *Generator) AddLinuxGIDMapping(hid, cid, size uint32) error { g.initSpecLinux() g.spec.Linux.GIDMappings = append(g.spec.Linux.GIDMappings, idMapping) - return nil } // SetLinuxRootPropagation sets g.spec.Linux.RootfsPropagation. @@ -807,11 +803,10 @@ func (g *Generator) ClearPreStartHooks() { } // AddPreStartHook add a prestart hook into g.spec.Hooks.Prestart. -func (g *Generator) AddPreStartHook(path string, args []string) error { +func (g *Generator) AddPreStartHook(path string, args []string) { g.initSpec() hook := rspec.Hook{Path: path, Args: args} g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook) - return nil } // ClearPostStopHooks clear g.spec.Hooks.Poststop. @@ -823,11 +818,10 @@ func (g *Generator) ClearPostStopHooks() { } // AddPostStopHook adds a poststop hook into g.spec.Hooks.Poststop. -func (g *Generator) AddPostStopHook(path string, args []string) error { +func (g *Generator) AddPostStopHook(path string, args []string) { g.initSpec() hook := rspec.Hook{Path: path, Args: args} g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook) - return nil } // ClearPostStartHooks clear g.spec.Hooks.Poststart. @@ -839,15 +833,14 @@ func (g *Generator) ClearPostStartHooks() { } // AddPostStartHook adds a poststart hook into g.spec.Hooks.Poststart. -func (g *Generator) AddPostStartHook(path string, args []string) error { +func (g *Generator) AddPostStartHook(path string, args []string) { g.initSpec() hook := rspec.Hook{Path: path, Args: args} g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook) - return nil } // AddTmpfsMount adds a tmpfs mount into g.spec.Mounts. -func (g *Generator) AddTmpfsMount(dest string, options []string) error { +func (g *Generator) AddTmpfsMount(dest string, options []string) { mnt := rspec.Mount{ Destination: dest, Type: "tmpfs", @@ -857,7 +850,6 @@ func (g *Generator) AddTmpfsMount(dest string, options []string) error { g.initSpec() g.spec.Mounts = append(g.spec.Mounts, mnt) - return nil } // AddCgroupsMount adds a cgroup mount into g.spec.Mounts. @@ -884,7 +876,7 @@ func (g *Generator) AddCgroupsMount(mountCgroupOption string) error { } // AddBindMount adds a bind mount into g.spec.Mounts. -func (g *Generator) AddBindMount(source, dest, options string) error { +func (g *Generator) AddBindMount(source, dest, options string) { if options == "" { options = "ro" } @@ -899,7 +891,6 @@ func (g *Generator) AddBindMount(source, dest, options string) error { } g.initSpec() g.spec.Mounts = append(g.spec.Mounts, mnt) - return nil } // SetupPrivileged sets up the priviledge-related fields inside g.spec. From 36ccd5c596ca930702a74a5771b24770d562c74a Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Fri, 29 Jul 2016 11:39:00 -0400 Subject: [PATCH 11/20] change param type of AddProcessAdditionalGid Signed-off-by: Haiyan Meng Backported to v1.0.0.rc1 from a99f698 #169 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 6 +++++- generate/generate.go | 14 ++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index 39c45567c..aaac7d769 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -174,7 +174,11 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { if context.IsSet("groups") { groups := context.StringSlice("groups") for _, group := range groups { - g.AddProcessAdditionalGid(group) + groupID, err := strconv.Atoi(group) + if err != nil { + return err + } + g.AddProcessAdditionalGid(uint32(groupID)) } } diff --git a/generate/generate.go b/generate/generate.go index dcc36a058..2ebd86956 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -330,20 +330,14 @@ func (g *Generator) ClearProcessAdditionalGids() { } // AddProcessAdditionalGid adds an additional gid into g.spec.Process.AdditionalGids. -func (g *Generator) AddProcessAdditionalGid(gid string) error { - groupID, err := strconv.Atoi(gid) - if err != nil { - return err - } - +func (g *Generator) AddProcessAdditionalGid(gid uint32) { g.initSpec() for _, group := range g.spec.Process.User.AdditionalGids { - if group == uint32(groupID) { - return nil + if group == gid { + return } } - g.spec.Process.User.AdditionalGids = append(g.spec.Process.User.AdditionalGids, uint32(groupID)) - return nil + g.spec.Process.User.AdditionalGids = append(g.spec.Process.User.AdditionalGids, gid) } // SetProcessSelinuxLabel sets g.spec.Process.SelinuxLabel. From ed659abc8baf0392859dda1afac6a54479340fc6 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Thu, 28 Jul 2016 19:05:38 +0800 Subject: [PATCH 12/20] generate: optimize namespace setup log and fix manpage Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from 15f529b #166 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 21 +++--------- man/ocitools-generate.1.md | 69 +++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index a6423a028..8bbf6cdec 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -228,13 +228,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { needsNewUser = true } - nsMaps := map[string]string{} - for _, nsName := range generate.Namespaces { - if context.IsSet(nsName) { - nsMaps[nsName] = context.String(nsName) - } - } - setupLinuxNamespaces(g, needsNewUser, nsMaps) + setupLinuxNamespaces(context, g, needsNewUser) if context.IsSet("tmpfs") { tmpfsSlice := context.StringSlice("tmpfs") @@ -366,19 +360,12 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { return nil } -func checkNs(nsMaps map[string]string, nsName string) bool { - if _, ok := nsMaps[nsName]; !ok { - return false - } - return true -} - -func setupLinuxNamespaces(g *generate.Generator, needsNewUser bool, nsMaps map[string]string) { +func setupLinuxNamespaces(context *cli.Context, g *generate.Generator, needsNewUser bool) { for _, nsName := range generate.Namespaces { - if !checkNs(nsMaps, nsName) && !(needsNewUser && nsName == "user") { + if !context.IsSet(nsName) && !(needsNewUser && nsName == "user") { continue } - nsPath := nsMaps[nsName] + nsPath := context.String(nsName) if nsPath == "host" { g.RemoveLinuxNamespace(nsName) continue diff --git a/man/ocitools-generate.1.md b/man/ocitools-generate.1.md index eb79b74ee..16b7ceea1 100644 --- a/man/ocitools-generate.1.md +++ b/man/ocitools-generate.1.md @@ -29,7 +29,7 @@ read the configuration from `config.json`. --args "/usr/bin/httpd" --args "-D" --args "FOREGROUND" -**--bind**=*[[HOST-DIR:CONTAINER-DIR][:OPTIONS]]* +**--bind**=*[[HOST-DIR:CONTAINER-DIR][:OPTIONS]]* Bind mount directories src:dest:(rw,ro) If you specify, ` --bind /HOST-DIR:/CONTAINER-DIR`, runc bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the OCI container. The `OPTIONS` are a comma @@ -45,10 +45,11 @@ read the configuration from `config.json`. **--cap-drop**=[] Drop Linux capabilities -**--cgroup**=[*PATH*] - Use a Cgroup namespace. If *PATH* is set, join that namespace. If it - is unset, create a new namespace. The special *PATH* `host` removes - any existing Cgroup namespace from the configuration. +**--cgroup**=*PATH* + Use a Cgroup namespace where *PATH* is an existing Cgroup namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing Cgroup namespace from + the configuration. **--cgroups-path**="" Specifies the path to the cgroups relative to the cgroups mount point. @@ -77,15 +78,17 @@ inside of the container. **--hostname**="" Set the container host name that is available inside the container. -**--ipc**=[*PATH*] - Use an IPC namespace. If *PATH* is set, join that namespace. If it - is unset, create a new namespace. The special *PATH* `host` removes - any existing IPC namespace from the configuration. +**--ipc**=*PATH* + Use an IPC namespace where *PATH* is an existing IPC namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing IPC namespace from the + configuration. -**--mount**=[*PATH*] - Use a mount namespace. If *PATH* is set, join that namespace. If - it is unset, create a new namespace. The special *PATH* `host` - removes any existing mount namespace from the configuration. +**--mount**=*PATH* + Use a mount namespace where *PATH* is an existing mount namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing mount namespace from the + configuration. **--mount-cgroups**=[rw|ro|no] Mount cgroups. The default is `no`. @@ -102,10 +105,11 @@ inside of the container. "system_u:object_r:usr_t:s0" might be a good label for a readonly container, "system_u:system_r:svirt_sandbox_file_t:s0:c1,c2" for a read/write container. -**--network**=[*PATH*] - Use a network namespace. If *PATH* is set, join that namespace. If - it is unset, create a new namespace. The special *PATH* `host` - removes any existing network namespace from the configuration. +**--network**=*PATH* + Use a network namespace where *PATH* is an existing network namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing network namespace from the + configuration. **--no-new-privileges** Set no new privileges bit for the container process. Setting this flag @@ -121,10 +125,11 @@ inside of the container. **--os**=OS Operating system used within the container -**--pid**=[*PATH*] - Use a PID namespace. If *PATH* is set, join that namespace. If it - is unset, create a new namespace. The special *PATH* `host` removes - any existing PID namespace from the configuration. +**--pid**=*PATH* + Use a PID namespace where *PATH* is an existing PID namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing PID namespace from + the configuration. **--poststart**=CMD Path to command to run in poststart hooks. This command will be run before @@ -143,7 +148,7 @@ inside of the container. Give extended privileges to this container. The default is *false*. By default, OCI containers are -“unprivileged” (=false) and cannot do some of the things a normal root process can do. +“unprivileged” (=false) and cannot do some of the things a normal root process can do. When the operator executes **ocitools generate --privileged**, OCI will enable access to all devices on the host as well as disable some of the confinement mechanisms like AppArmor, SELinux, and seccomp from blocking access to privileged processes. This gives the container processes nearly all the same access to the host as processes generating outside of a container on the host. @@ -214,15 +219,17 @@ inside of the container. **--uidmappings** Add UIDMappings e.g HostUID:ContainerID:Size. Implies **--user=**. -**--user**=[*PATH*] - Use a user namespace. If *PATH* is set, join that namespace. If it - is unset, create a new namespace. The special *PATH* `host` removes - any existing user namespace from the configuration. - -**--uts**=[*PATH*] - Use a UTS namespace. If *PATH* is set, join that namespace. If it - is unset, create a new namespace. The special *PATH* `host` removes - any existing UTS namespace from the configuration. +**--user**=*PATH* + Use a user namespace where *PATH* is an existing user namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing user namespace from + the configuration. + +**--uts**=*PATH* + Use a UTS namespace where *PATH* is an existing UTS namespace file + to join. The special *PATH* empty-string creates a new namespace. + The special *PATH* `host` removes any existing UTS namespace from + the configuration. # EXAMPLES From ff90d0111ef7aade1c7089ea5d82a9496b83e36d Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Mon, 1 Aug 2016 21:12:41 -0400 Subject: [PATCH 13/20] update urfave/cli package to v1.18.0 The new version of urfave/cli supports Int64Flag and Uint64Flag. These flag types are useful for setting cpu/mem resources. Signed-off-by: Haiyan Meng Backported to v1.0.0.rc1 from dc3404e #174 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- Godeps/Godeps.json | 4 +- .../src/github.com/urfave/cli/.gitignore | 1 + .../src/github.com/urfave/cli/.travis.yml | 8 + .../src/github.com/urfave/cli/CHANGELOG.md | 10 +- .../src/github.com/urfave/cli/LICENSE | 28 +-- .../src/github.com/urfave/cli/README.md | 72 +++++- .../src/github.com/urfave/cli/app.go | 27 +- .../src/github.com/urfave/cli/context.go | 105 +++++++- .../src/github.com/urfave/cli/flag.go | 233 +++++++++++++++++- .../src/github.com/urfave/cli/help.go | 3 +- .../src/github.com/urfave/cli/runtests | 8 +- 11 files changed, 455 insertions(+), 44 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 947f5121d..7dd60b0ed 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -24,8 +24,8 @@ }, { "ImportPath": "github.com/urfave/cli", - "Comment": "v1.17.0-57-gedb24d0", - "Rev": "edb24d02aa3cea2319c33f2836d4a5133907fe4c" + "Comment": "v1.18.0", + "Rev": "1efa31f08b9333f1bd4882d61f9d668a70cd902e" } ] } diff --git a/Godeps/_workspace/src/github.com/urfave/cli/.gitignore b/Godeps/_workspace/src/github.com/urfave/cli/.gitignore index 782377890..faf70c4c2 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/.gitignore +++ b/Godeps/_workspace/src/github.com/urfave/cli/.gitignore @@ -1 +1,2 @@ *.coverprofile +node_modules/ diff --git a/Godeps/_workspace/src/github.com/urfave/cli/.travis.yml b/Godeps/_workspace/src/github.com/urfave/cli/.travis.yml index 657e96a9d..273d017b4 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/.travis.yml +++ b/Godeps/_workspace/src/github.com/urfave/cli/.travis.yml @@ -2,6 +2,10 @@ language: go sudo: false +cache: + directories: + - node_modules + go: - 1.2.2 - 1.3.3 @@ -25,8 +29,12 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... +- if [ ! -f node_modules/.bin/markdown-toc ] ; then + npm install markdown-toc ; + fi script: - ./runtests vet - ./runtests test - ./runtests gfmxr +- ./runtests toc diff --git a/Godeps/_workspace/src/github.com/urfave/cli/CHANGELOG.md b/Godeps/_workspace/src/github.com/urfave/cli/CHANGELOG.md index ac6d73a78..d1904fe0b 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/CHANGELOG.md +++ b/Godeps/_workspace/src/github.com/urfave/cli/CHANGELOG.md @@ -3,10 +3,13 @@ **ATTN**: This project uses [semantic versioning](http://semver.org/). ## [Unreleased] + +## [1.18.0] - 2016-06-27 ### Added - `./runtests` test runner with coverage tracking by default - testing on OS X - testing on Windows +- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code ### Changed - Use spaces for alignment in help/usage output instead of tabs, making the @@ -14,6 +17,10 @@ ### Fixed - Printing of command aliases in help text +- Printing of visible flags for both struct and struct pointer flags +- Display the `help` subcommand when using `CommandCategories` +- No longer swallows `panic`s that occur within the `Action`s themselves when + detecting the signature of the `Action` field ## [1.17.0] - 2016-05-09 ### Added @@ -294,7 +301,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.17.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD +[1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 [1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 [1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0 diff --git a/Godeps/_workspace/src/github.com/urfave/cli/LICENSE b/Godeps/_workspace/src/github.com/urfave/cli/LICENSE index 5515ccfb7..42a597e29 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/LICENSE +++ b/Godeps/_workspace/src/github.com/urfave/cli/LICENSE @@ -1,21 +1,21 @@ -Copyright (C) 2013 Jeremy Saenz -All Rights Reserved. +MIT License -MIT LICENSE +Copyright (c) 2016 Jeremy Saenz & Contributors -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/urfave/cli/README.md b/Godeps/_workspace/src/github.com/urfave/cli/README.md index 9bbfe836f..ebb1d7413 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/README.md +++ b/Godeps/_workspace/src/github.com/urfave/cli/README.md @@ -1,3 +1,6 @@ +cli +=== + [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) @@ -6,9 +9,6 @@ [![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / [![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) - -# cli - **Notice:** This is the library formerly known as `github.com/codegangsta/cli` -- Github will automatically redirect requests to this repository, but we recommend updating your references for clarity. @@ -17,6 +17,37 @@ cli is a simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. + + +- [Overview](#overview) +- [Installation](#installation) + * [Supported platforms](#supported-platforms) + * [Using the `v2` branch](#using-the-v2-branch) + * [Pinning to the `v1` branch](#pinning-to-the-v1-branch) +- [Getting Started](#getting-started) +- [Examples](#examples) + * [Arguments](#arguments) + * [Flags](#flags) + + [Placeholder Values](#placeholder-values) + + [Alternate Names](#alternate-names) + + [Values from the Environment](#values-from-the-environment) + + [Values from alternate input sources (YAML and others)](#values-from-alternate-input-sources-yaml-and-others) + * [Subcommands](#subcommands) + * [Subcommands categories](#subcommands-categories) + * [Exit code](#exit-code) + * [Bash Completion](#bash-completion) + + [Enabling](#enabling) + + [Distribution](#distribution) + + [Customization](#customization) + * [Generated Help Text](#generated-help-text) + + [Customization](#customization-1) + * [Version Flag](#version-flag) + + [Customization](#customization-2) + + [Full API Example](#full-api-example) +- [Contribution Guidelines](#contribution-guidelines) + + + ## Overview Command line apps are usually so tiny that there is absolutely no reason why @@ -53,6 +84,8 @@ released version of Go on OS X and Windows. For full details, see ### Using the `v2` branch +**Warning**: The `v2` branch is currently unreleased and considered unstable. + There is currently a long-lived branch named `v2` that is intended to land as the new `master` branch once development there has settled down. The current `master` branch (mirrored as `v1`) is being manually merged into `v2` on @@ -211,7 +244,7 @@ COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS - --version Shows version information + --version Shows version information ``` ### Arguments @@ -980,11 +1013,11 @@ func main() { #### Full API Example -**NOTE**: This is a contrived (functioning) example meant strictly for API +**Notice**: This is a contrived (functioning) example meant strictly for API demonstration purposes. Use of one's imagination is encouraged. ``` go package main @@ -1036,6 +1069,19 @@ func (w *hexWriter) Write(p []byte) (int, error) { return len(p), nil } +type genericType struct{ + s string +} + +func (g *genericType) Set(value string) error { + g.s = value + return nil +} + +func (g *genericType) String() string { + return g.s +} + func main() { app := cli.NewApp() app.Name = "kənˈtrīv" @@ -1105,7 +1151,17 @@ func main() { app.Flags = []cli.Flag{ cli.BoolFlag{Name: "fancy"}, cli.BoolTFlag{Name: "fancier"}, + cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3}, + cli.Float64Flag{Name: "howmuch"}, + cli.GenericFlag{Name: "wat", Value: &genericType{}}, + cli.Int64Flag{Name: "longdistance"}, + cli.Int64SliceFlag{Name: "intervals"}, + cli.IntFlag{Name: "distance"}, + cli.IntSliceFlag{Name: "times"}, cli.StringFlag{Name: "dance-move, d"}, + cli.StringSliceFlag{Name: "names, N"}, + cli.UintFlag{Name: "age"}, + cli.Uint64Flag{Name: "bigage"}, } app.EnableBashCompletion = true app.HideHelp = false @@ -1182,10 +1238,14 @@ func main() { fmt.Printf("%#v\n", nc.Duration("howlong")) fmt.Printf("%#v\n", nc.Float64("hay")) fmt.Printf("%#v\n", nc.Generic("bloop")) + fmt.Printf("%#v\n", nc.Int64("bonk")) + fmt.Printf("%#v\n", nc.Int64Slice("burnks")) fmt.Printf("%#v\n", nc.Int("bips")) fmt.Printf("%#v\n", nc.IntSlice("blups")) fmt.Printf("%#v\n", nc.String("snurt")) fmt.Printf("%#v\n", nc.StringSlice("snurkles")) + fmt.Printf("%#v\n", nc.Uint("flub")) + fmt.Printf("%#v\n", nc.Uint64("florb")) fmt.Printf("%#v\n", nc.GlobalBool("global-nope")) fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp")) fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong")) diff --git a/Godeps/_workspace/src/github.com/urfave/cli/app.go b/Godeps/_workspace/src/github.com/urfave/cli/app.go index 9c7f679cb..a046c0128 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/app.go +++ b/Godeps/_workspace/src/github.com/urfave/cli/app.go @@ -8,6 +8,7 @@ import ( "path/filepath" "reflect" "sort" + "strings" "time" ) @@ -139,13 +140,6 @@ func (a *App) Setup() { } a.Commands = newCmds - a.categories = CommandCategories{} - for _, command := range a.Commands { - a.categories = a.categories.AddCommand(command.Category, command) - } - sort.Sort(a.categories) - - // append help to commands if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) if (HelpFlag != BoolFlag{}) { @@ -153,7 +147,6 @@ func (a *App) Setup() { } } - //append version/help flags if a.EnableBashCompletion { a.appendFlag(BashCompletionFlag) } @@ -161,6 +154,12 @@ func (a *App) Setup() { if !a.HideVersion { a.appendFlag(VersionFlag) } + + a.categories = CommandCategories{} + for _, command := range a.Commands { + a.categories = a.categories.AddCommand(command.Category, command) + } + sort.Sort(a.categories) } // Run is the entry point to the cli app. Parses the arguments slice and routes @@ -464,11 +463,13 @@ func (a Author) String() string { func HandleAction(action interface{}, context *Context) (err error) { defer func() { if r := recover(); r != nil { - switch r.(type) { - case error: - err = r.(error) - default: - err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2) + // Try to detect a known reflection error from *this scope*, rather than + // swallowing all panics that may happen when calling an Action func. + s := fmt.Sprintf("%v", r) + if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") { + err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2) + } else { + panic(r) } } }() diff --git a/Godeps/_workspace/src/github.com/urfave/cli/context.go b/Godeps/_workspace/src/github.com/urfave/cli/context.go index c34246369..879bae5ec 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/context.go +++ b/Godeps/_workspace/src/github.com/urfave/cli/context.go @@ -31,6 +31,21 @@ func (c *Context) Int(name string) int { return lookupInt(name, c.flagSet) } +// Int64 looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Int64(name string) int64 { + return lookupInt64(name, c.flagSet) +} + +// Uint looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Uint(name string) uint { + return lookupUint(name, c.flagSet) +} + +// Uint64 looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Uint64(name string) uint64 { + return lookupUint64(name, c.flagSet) +} + // Duration looks up the value of a local time.Duration flag, returns 0 if no // time.Duration flag exists func (c *Context) Duration(name string) time.Duration { @@ -70,6 +85,12 @@ func (c *Context) IntSlice(name string) []int { return lookupIntSlice(name, c.flagSet) } +// Int64Slice looks up the value of a local int slice flag, returns nil if no int +// slice flag exists +func (c *Context) Int64Slice(name string) []int64 { + return lookupInt64Slice(name, c.flagSet) +} + // Generic looks up the value of a local generic flag, returns nil if no generic // flag exists func (c *Context) Generic(name string) interface{} { @@ -84,6 +105,30 @@ func (c *Context) GlobalInt(name string) int { return 0 } +// GlobalInt64 looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalInt64(name string) int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64(name, fs) + } + return 0 +} + +// GlobalUint looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalUint(name string) uint { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint(name, fs) + } + return 0 +} + +// GlobalUint64 looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalUint64(name string) uint64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint64(name, fs) + } + return 0 +} + // GlobalFloat64 looks up the value of a global float64 flag, returns float64(0) // if no float64 flag exists func (c *Context) GlobalFloat64(name string) float64 { @@ -147,6 +192,15 @@ func (c *Context) GlobalIntSlice(name string) []int { return nil } +// GlobalInt64Slice looks up the value of a global int slice flag, returns nil if +// no int slice flag exists +func (c *Context) GlobalInt64Slice(name string) []int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64Slice(name, fs) + } + return nil +} + // GlobalGeneric looks up the value of a global generic flag, returns nil if no // generic flag exists func (c *Context) GlobalGeneric(name string) interface{} { @@ -306,7 +360,46 @@ func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { func lookupInt(name string, set *flag.FlagSet) int { f := set.Lookup(name) if f != nil { - val, err := strconv.Atoi(f.Value.String()) + val, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return int(val) + } + + return 0 +} + +func lookupInt64(name string, set *flag.FlagSet) int64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupUint(name string, set *flag.FlagSet) uint { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return uint(val) + } + + return 0 +} + +func lookupUint64(name string, set *flag.FlagSet) uint64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } @@ -370,6 +463,16 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int { return nil } +func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*Int64Slice)).Value() + + } + + return nil +} + func lookupGeneric(name string, set *flag.FlagSet) interface{} { f := set.Lookup(name) if f != nil { diff --git a/Godeps/_workspace/src/github.com/urfave/cli/flag.go b/Godeps/_workspace/src/github.com/urfave/cli/flag.go index 1e8112e7e..f8a28d119 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/flag.go +++ b/Godeps/_workspace/src/github.com/urfave/cli/flag.go @@ -189,7 +189,7 @@ func (f *IntSlice) Set(value string) error { // String returns a readable representation of this value (for usage defaults) func (f *IntSlice) String() string { - return fmt.Sprintf("%d", *f) + return fmt.Sprintf("%#v", *f) } // Value returns the slice of ints set by this flag @@ -245,6 +245,77 @@ func (f IntSliceFlag) GetName() string { return f.Name } +// Int64Slice is an opaque type for []int to satisfy flag.Value +type Int64Slice []int64 + +// Set parses the value into an integer and appends it to the list of values +func (f *Int64Slice) Set(value string) error { + tmp, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + *f = append(*f, tmp) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Int64Slice) String() string { + return fmt.Sprintf("%#v", *f) +} + +// Value returns the slice of ints set by this flag +func (f *Int64Slice) Value() []int64 { + return *f +} + +// Int64SliceFlag is an int flag that can be specified multiple times on the +// command-line +type Int64SliceFlag struct { + Name string + Value *Int64Slice + Usage string + EnvVar string + Hidden bool +} + +// String returns the usage +func (f Int64SliceFlag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f Int64SliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + newVal := &Int64Slice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + err := newVal.Set(s) + if err != nil { + fmt.Fprintf(ErrWriter, err.Error()) + } + } + f.Value = newVal + break + } + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &Int64Slice{} + } + set.Var(f.Value, name, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f Int64SliceFlag) GetName() string { + return f.Name +} + // BoolFlag is a switch that defaults to false type BoolFlag struct { Name string @@ -376,7 +447,6 @@ func (f StringFlag) GetName() string { } // IntFlag is a flag that takes an integer -// Errors if the value provided cannot be parsed type IntFlag struct { Name string Value int @@ -420,6 +490,138 @@ func (f IntFlag) GetName() string { return f.Name } +// Int64Flag is a flag that takes a 64-bit integer +type Int64Flag struct { + Name string + Value int64 + Usage string + EnvVar string + Destination *int64 + Hidden bool +} + +// String returns the usage +func (f Int64Flag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f Int64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + if err == nil { + f.Value = envValInt + break + } + } + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Int64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Int64(name, f.Value, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f Int64Flag) GetName() string { + return f.Name +} + +// UintFlag is a flag that takes an unsigned integer +type UintFlag struct { + Name string + Value uint + Usage string + EnvVar string + Destination *uint + Hidden bool +} + +// String returns the usage +func (f UintFlag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f UintFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err == nil { + f.Value = uint(envValInt) + break + } + } + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.UintVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint(name, f.Value, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f UintFlag) GetName() string { + return f.Name +} + +// Uint64Flag is a flag that takes an unsigned 64-bit integer +type Uint64Flag struct { + Name string + Value uint64 + Usage string + EnvVar string + Destination *uint64 + Hidden bool +} + +// String returns the usage +func (f Uint64Flag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f Uint64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err == nil { + f.Value = uint64(envValInt) + break + } + } + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Uint64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint64(name, f.Value, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f Uint64Flag) GetName() string { + return f.Name +} + // DurationFlag is a flag that takes a duration specified in Go's duration // format: https://golang.org/pkg/time/#ParseDuration type DurationFlag struct { @@ -466,7 +668,6 @@ func (f DurationFlag) GetName() string { } // Float64Flag is a flag that takes an float value -// Errors if the value provided cannot be parsed type Float64Flag struct { Name string Value float64 @@ -512,7 +713,7 @@ func (f Float64Flag) GetName() string { func visibleFlags(fl []Flag) []Flag { visible := []Flag{} for _, flag := range fl { - if !reflect.ValueOf(flag).FieldByName("Hidden").Bool() { + if !flagValue(flag).FieldByName("Hidden").Bool() { visible = append(visible, flag) } } @@ -578,13 +779,24 @@ func withEnvHint(envVar, str string) string { return str + envText } -func stringifyFlag(f Flag) string { +func flagValue(f Flag) reflect.Value { fv := reflect.ValueOf(f) + for fv.Kind() == reflect.Ptr { + fv = reflect.Indirect(fv) + } + return fv +} + +func stringifyFlag(f Flag) string { + fv := flagValue(f) switch f.(type) { case IntSliceFlag: return withEnvHint(fv.FieldByName("EnvVar").String(), stringifyIntSliceFlag(f.(IntSliceFlag))) + case Int64SliceFlag: + return withEnvHint(fv.FieldByName("EnvVar").String(), + stringifyInt64SliceFlag(f.(Int64SliceFlag))) case StringSliceFlag: return withEnvHint(fv.FieldByName("EnvVar").String(), stringifyStringSliceFlag(f.(StringSliceFlag))) @@ -630,6 +842,17 @@ func stringifyIntSliceFlag(f IntSliceFlag) string { return stringifySliceFlag(f.Usage, f.Name, defaultVals) } +func stringifyInt64SliceFlag(f Int64SliceFlag) string { + defaultVals := []string{} + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) + } + } + + return stringifySliceFlag(f.Usage, f.Name, defaultVals) +} + func stringifyStringSliceFlag(f StringSliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { diff --git a/Godeps/_workspace/src/github.com/urfave/cli/help.go b/Godeps/_workspace/src/github.com/urfave/cli/help.go index f2ddc5499..0f4cf14cd 100644 --- a/Godeps/_workspace/src/github.com/urfave/cli/help.go +++ b/Godeps/_workspace/src/github.com/urfave/cli/help.go @@ -117,8 +117,9 @@ var HelpPrinter helpPrinter = printHelp var VersionPrinter = printVersion // ShowAppHelp is an action that displays the help. -func ShowAppHelp(c *Context) { +func ShowAppHelp(c *Context) error { HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) + return nil } // DefaultAppComplete prints the list of subcommands as the default app completion method diff --git a/Godeps/_workspace/src/github.com/urfave/cli/runtests b/Godeps/_workspace/src/github.com/urfave/cli/runtests index 72c1f0ddf..0a7b483e3 100755 --- a/Godeps/_workspace/src/github.com/urfave/cli/runtests +++ b/Godeps/_workspace/src/github.com/urfave/cli/runtests @@ -18,7 +18,8 @@ def main(sysargs=sys.argv[:]): targets = { 'vet': _vet, 'test': _test, - 'gfmxr': _gfmxr + 'gfmxr': _gfmxr, + 'toc': _toc, } parser = argparse.ArgumentParser() @@ -62,6 +63,11 @@ def _vet(): _run('go vet ./...'.split()) +def _toc(): + _run(['node_modules/.bin/markdown-toc', '-i', 'README.md']) + _run(['git', 'diff', '--quiet']) + + def _run(command): print('runtests: {}'.format(' '.join(command)), file=sys.stderr) check_call(command) From 119586ea4466af0be5848ca476d3cb7ce823e752 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Thu, 28 Jul 2016 23:26:19 +0800 Subject: [PATCH 14/20] completions: update based on generate help message Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from 2c278ff #167 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- completions/bash/ocitools | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/completions/bash/ocitools b/completions/bash/ocitools index 3328792d7..50233c71a 100644 --- a/completions/bash/ocitools +++ b/completions/bash/ocitools @@ -275,6 +275,7 @@ _ocitools_generate() { --cap-add --cap-drop --cgroup + --cgroup-path --cwd --env --gid @@ -283,25 +284,31 @@ _ocitools_generate() { --hostname --help --ipc + --label --mount --mount-cgroups + --mount-label --network --os + --output --pid --poststart --poststop --prestart --root-propagation --rootfs - --seccomp-default + --seccomp-allow --seccomp-arch + --seccomp-default + --seccomp-errno --seccomp-syscalls --selinux-label - --mount-label --sysctl + --tmplate --tmpfs --uid --uidmappings + --user --uts " @@ -330,11 +337,6 @@ _ocitools_generate() { return ;; - --seccomp-default) - __ocitools_complete_seccomp_actions - return - ;; - --root-propagation) __ocitools_complete_propagations return From 56a65bde66b8c97308cd74d43820188b1e120b3d Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Wed, 27 Jul 2016 18:34:47 +0800 Subject: [PATCH 15/20] generate: fix mount-cgroups bug Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from c4fb2c0 #156 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- generate/generate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/generate/generate.go b/generate/generate.go index 2ebd86956..1cc1b51e5 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -851,6 +851,7 @@ func (g *Generator) AddCgroupsMount(mountCgroupOption string) error { switch mountCgroupOption { case "ro": case "rw": + break case "no": return nil default: From de97cc850829dd4f494893a09db94671ebf283b2 Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Tue, 2 Aug 2016 16:29:22 -0400 Subject: [PATCH 16/20] support setting oom_score_adj Signed-off-by: Haiyan Meng Backported to v1.0.0.rc1 from 1915407 #176 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 5 +++++ generate/generate.go | 6 ++++++ man/ocitools-generate.1.md | 3 +++ 3 files changed, 14 insertions(+) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index e036cbf74..05e7a8dfb 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -58,6 +58,7 @@ var generateFlags = []cli.Flag{ cli.StringSliceFlag{Name: "seccomp-errno", Usage: "specifies syscalls to be added to list that returns an error"}, cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"}, cli.StringSliceFlag{Name: "label", Usage: "add annotations to the configuration e.g. key=value"}, + cli.IntFlag{Name: "oom-score-adj", Usage: "oom_score_adj for the container"}, } var generateCommand = cli.Command{ @@ -319,6 +320,10 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { g.AddLinuxGIDMapping(hid, cid, size) } + if context.IsSet("oom-score-adj") { + g.SetLinuxResourcesOOMScoreAdj(context.Int("oom-score-adj")) + } + var sd string var sa, ss []string diff --git a/generate/generate.go b/generate/generate.go index 1cc1b51e5..10c7667ca 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -358,6 +358,12 @@ func (g *Generator) SetLinuxMountLabel(label string) { g.spec.Linux.MountLabel = label } +// SetLinuxResourcesOOMScoreAdj sets g.spec.Linux.Resources.OOMScoreAdj. +func (g *Generator) SetLinuxResourcesOOMScoreAdj(adj int) { + g.initSpecLinuxResources() + g.spec.Linux.Resources.OOMScoreAdj = &adj +} + // SetLinuxResourcesCPUShares sets g.spec.Linux.Resources.CPU.Shares. func (g *Generator) SetLinuxResourcesCPUShares(shares uint64) { g.initSpecLinuxResourcesCPU() diff --git a/man/ocitools-generate.1.md b/man/ocitools-generate.1.md index 16b7ceea1..3e7fedce4 100644 --- a/man/ocitools-generate.1.md +++ b/man/ocitools-generate.1.md @@ -117,6 +117,9 @@ inside of the container. using tools like setuid apps. It is a good idea to run unprivileged containers with this flag. +**--oom-score-adj**=adj + Specifies oom_score_adj for the container. + **--output**=PATH Instead of writing the configuration JSON to stdout, write it to a file at *PATH* (overwriting the existing content if a file already From a336a00c34fdcd9949471c4e127b2d0f818095e7 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Mon, 15 Aug 2016 10:49:45 +0800 Subject: [PATCH 17/20] add label manpage and fix help Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from 5de50e5 #189 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/generate.go | 2 +- man/ocitools-generate.1.md | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go index 05e7a8dfb..3911bbfb1 100644 --- a/cmd/ocitools/generate.go +++ b/cmd/ocitools/generate.go @@ -36,7 +36,7 @@ var generateFlags = []cli.Flag{ cli.StringFlag{Name: "mount-label", Usage: "selinux mount context label"}, cli.StringSliceFlag{Name: "tmpfs", Usage: "mount tmpfs"}, cli.StringSliceFlag{Name: "args", Usage: "command to run in the container"}, - cli.StringSliceFlag{Name: "env", Usage: "add environment variable"}, + cli.StringSliceFlag{Name: "env", Usage: "add environment variable e.g. key=value"}, cli.StringFlag{Name: "cgroups-path", Usage: "specify the path to the cgroups"}, cli.StringFlag{Name: "mount-cgroups", Value: "no", Usage: "mount cgroups (rw,ro,no)"}, cli.StringSliceFlag{Name: "bind", Usage: "bind mount directories src:dest:(rw,ro)"}, diff --git a/man/ocitools-generate.1.md b/man/ocitools-generate.1.md index 3e7fedce4..138fe0aad 100644 --- a/man/ocitools-generate.1.md +++ b/man/ocitools-generate.1.md @@ -58,10 +58,10 @@ read the configuration from `config.json`. Current working directory for the process **--env**=[] - Set environment variables - This option allows you to specify arbitrary -environment variables that are available for the process that will be launched -inside of the container. + Set environment variables e.g. key=value. + This option allows you to specify arbitrary environment variables + that are available for the process that will be launched inside of + the container. **--gid**=GID Gid for the process inside of container @@ -84,6 +84,9 @@ inside of the container. The special *PATH* `host` removes any existing IPC namespace from the configuration. +**--label**=[] + Add annotations to the configuration e.g. key=value. + **--mount**=*PATH* Use a mount namespace where *PATH* is an existing mount namespace file to join. The special *PATH* empty-string creates a new namespace. From f4bba5af4d5bc1e77393e011f1402549f21204cb Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Wed, 17 Aug 2016 17:23:22 +0800 Subject: [PATCH 18/20] validation: add linux resource check Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from 9cad4d8 #195 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/validate.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cmd/ocitools/validate.go b/cmd/ocitools/validate.go index 0fab50202..246979aab 100644 --- a/cmd/ocitools/validate.go +++ b/cmd/ocitools/validate.go @@ -361,6 +361,11 @@ func checkLinux(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) } } + if spec.Linux.Resources != nil { + ms := checkLinuxResources(*spec.Linux.Resources, hostCheck) + msgs = append(msgs, ms...) + } + if spec.Linux.Seccomp != nil { ms := checkSeccomp(*spec.Linux.Seccomp) msgs = append(msgs, ms...) @@ -381,6 +386,21 @@ func checkLinux(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) return } +func checkLinuxResources(r rspec.Resources, hostCheck bool) (msgs []string) { + logrus.Debugf("check linux resources") + + if r.Memory != nil { + if r.Memory.Limit != nil && r.Memory.Swap != nil && uint64(*r.Memory.Limit) > uint64(*r.Memory.Swap) { + msgs = append(msgs, fmt.Sprintf("Minimum memoryswap should be larger than memory limit")) + } + if r.Memory.Limit != nil && r.Memory.Reservation != nil && uint64(*r.Memory.Reservation) > uint64(*r.Memory.Limit) { + msgs = append(msgs, fmt.Sprintf("Minimum memory limit should be larger than memory reservation")) + } + } + + return +} + func checkSeccomp(s rspec.Seccomp) (msgs []string) { logrus.Debugf("check seccomp") From d0206067b0c31ec5b2369dd7078406734f657812 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Tue, 23 Aug 2016 15:11:34 +0800 Subject: [PATCH 19/20] add namespace check for uid/gid mappings Signed-off-by: Ma Shimiao Backported to v1.0.0.rc1 from bf50b73b #199 (cherry-pick applied cleanly). Signed-off-by: W. Trevor King --- cmd/ocitools/validate.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd/ocitools/validate.go b/cmd/ocitools/validate.go index 246979aab..6c37147a5 100644 --- a/cmd/ocitools/validate.go +++ b/cmd/ocitools/validate.go @@ -316,13 +316,7 @@ func checkLinux(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) ipcExists := false mountExists := false netExists := false - - if len(spec.Linux.UIDMappings) > 5 { - msgs = append(msgs, "Only 5 UID mappings are allowed (linux kernel restriction).") - } - if len(spec.Linux.GIDMappings) > 5 { - msgs = append(msgs, "Only 5 GID mappings are allowed (linux kernel restriction).") - } + userExists := false for index := 0; index < len(spec.Linux.Namespaces); index++ { if !namespaceValid(spec.Linux.Namespaces[index]) { @@ -336,10 +330,20 @@ func checkLinux(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) netExists = true } else if spec.Linux.Namespaces[index].Type == rspec.MountNamespace { mountExists = true + } else if spec.Linux.Namespaces[index].Type == rspec.UserNamespace { + userExists = true } } } + if (len(spec.Linux.UIDMappings) > 0 || len(spec.Linux.GIDMappings) > 0) && !userExists { + msgs = append(msgs, "UID/GID mappings requires a new User namespace to be specified as well") + } else if len(spec.Linux.UIDMappings) > 5 { + msgs = append(msgs, "Only 5 UID mappings are allowed (linux kernel restriction).") + } else if len(spec.Linux.GIDMappings) > 5 { + msgs = append(msgs, "Only 5 GID mappings are allowed (linux kernel restriction).") + } + for k := range spec.Linux.Sysctl { if strings.HasPrefix(k, "net.") && !netExists { msgs = append(msgs, fmt.Sprintf("Sysctl %v requires a new Network namespace to be specified as well", k)) From b6d953a41828df972a3c3372c0c5b1f4ee10409f Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 30 Aug 2016 23:34:43 -0700 Subject: [PATCH 20/20] man/ocitools.1: Replace "**...(1)**" with "**...**(1)" Before this commit the latter was slightly more popular: $ git grep '\*\*(1)' | wc -l 3 $ git grep '(1)\*\*' | wc -l 2 Change generated with: $ sed -i 's/(1)\*\*/**(1)/g' $(git grep -l '(1)\*\*') Signed-off-by: W. Trevor King --- man/ocitools.1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/ocitools.1.md b/man/ocitools.1.md index a698fff4e..dbc790cd0 100644 --- a/man/ocitools.1.md +++ b/man/ocitools.1.md @@ -38,11 +38,11 @@ ocitools is a collection of tools for working with the [OCI runtime specificatio # COMMANDS **validate** Validating OCI bundle - See **ocitools-validate(1)** for full documentation on the **validate** command. + See **ocitools-validate**(1) for full documentation on the **validate** command. **generate** Generating OCI runtime spec configuration files - See **ocitools-generate(1)** for full documentation on the **generate** command. + See **ocitools-generate**(1) for full documentation on the **generate** command. # SEE ALSO **ocitools-validate**(1), **ocitools-generate**(1)