Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Commit

Permalink
Initial implementation: nocs exec can run echo.
Browse files Browse the repository at this point in the history
We currently depend on https://github.com/cf-guardian/specs which is a
fork of https://github.com/opencontainers/specs and which may be
superseded by the pull request
opencontainers/runtime-spec#135.

[#101501242]

Signed-off-by: Gareth Smith <gsmith@pivotal.io>
  • Loading branch information
glyn authored and totherme committed Sep 2, 2015
1 parent a3ba0d6 commit d2b9e4c
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 2 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# nocs
A containerless implementation of the OCI Open Container Specification
# nOCS
A containerless implementation of the OCI Open Container Specification.

This is intended for testing [runc](https://github.com/opencontainers/runc), and other
[OCF](https://github.com/opencontainers/specs) clients without creating containers.
68 changes: 68 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"syscall"

"github.com/cf-guardian/specs"
)

func main() {
if os.Args[1] != "exec" {
panic("Unsupported command!")
}
config := parseConfig(configFilePath())

cmd := exec.Command(config.Process.Args[0], config.Process.Args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

checkError(cmd.Start(), "starting process", 80)

checkWaitError(cmd.Wait(), "awaiting process completion", 85)
}

func configFilePath() string {
if len(os.Args) < 3 || os.Args[2] == "" {
pwd, err := os.Getwd()
checkError(err, "getting working directory", 90)
return path.Join(pwd, "config.json")
}
return os.Args[2]
}

func parseConfig(configPath string) *specs.Spec {
configStr, err := ioutil.ReadFile(configPath)
checkError(err, "reading config file", 95)

var config = &specs.Spec{}
checkError(json.Unmarshal([]byte(configStr), config), "parsing config JSON", 100)

return config
}

func checkWaitError(err error, action string, exitCode int) {
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
if waitStatus, ok := exitErr.Sys().(syscall.WaitStatus); !ok {
checkError(err, action+": expected a WaitStatus", exitCode)
} else {
os.Exit(waitStatus.ExitStatus())
}
}
checkError(err, action+": expected an ExitError", exitCode)
}
}

func checkError(err error, action string, exitCode int) {
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %s: %s\n", filepath.Base(os.Args[0]), action, err)
os.Exit(exitCode)
}
}
27 changes: 27 additions & 0 deletions nocs_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"testing"

"github.com/onsi/gomega/gexec"
)

var nocsBin string

func TestNocs(t *testing.T) {
RegisterFailHandler(Fail)

SynchronizedBeforeSuite(func() []byte {
nocsPath, err := gexec.Build("github.com/cloudfoundry-incubator/nocs")
Expect(err).ToNot(HaveOccurred())
return []byte(nocsPath)
}, func(path []byte) {
Expect(string(path)).NotTo(BeEmpty())
nocsBin = string(path)
})

RunSpecs(t, "nOCS Suite")
}
208 changes: 208 additions & 0 deletions nocs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package main_test

import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"path"
"strings"
"syscall"

"path/filepath"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("nOCS", func() {
var (
configFileDir string
nocsProcessWD string

cmd *exec.Cmd

stdout, stderr string
exitCode int
)

BeforeEach(func() {
dirtyPath, err := ioutil.TempDir("", "nOCSTest")
Expect(err).NotTo(HaveOccurred())
configFileDir, err = filepath.EvalSymlinks(dirtyPath)
Expect(err).NotTo(HaveOccurred())

nocsProcessWD, err = os.Getwd()
Expect(err).NotTo(HaveOccurred())
})

JustBeforeEach(func() {
cmd.Dir = nocsProcessWD

outBuffer, errBuffer := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd.Stdout, cmd.Stderr = outBuffer, errBuffer

Expect(cmd.Start()).To(Succeed())

exitCode = getExitCode(cmd.Wait())

stdout, stderr = outBuffer.String(), errBuffer.String()
})

AfterEach(func() {
os.RemoveAll(configFileDir)
})

Context("Running a simple command", func() {
BeforeEach(func() {
commandArgs := []string{"echo", "Hello OCF!"}
configFilePath := createConfigFile("nocs-simple.json", configFileDir, commandArgs)

cmd = exec.Command(nocsBin, "exec", configFilePath)
})

It("produces the appropriate stdout", func() {
Expect(stdout).To(Equal("Hello OCF!\n"))
})

It("produces the appropriate stderr", func() {
Expect(stderr).To(BeEmpty())
})

It("returns the appropriate exit status code", func() {
Expect(exitCode).To(Equal(0))
})
})

Context("Running a command which produces stderr", func() {
BeforeEach(func() {
commandArgs := []string{"/bin/sh", "-c", "echo Hello OCF! 1>&2"}
configFilePath := createConfigFile("nocs-stderr.json", configFileDir, commandArgs)

cmd = exec.Command(nocsBin, "exec", configFilePath)
})

It("produces the appropriate stdout", func() {
Expect(stdout).To(BeEmpty())
})

It("produces the appropriate stderr", func() {
Expect(stderr).To(Equal("Hello OCF!\n"))
})

It("returns the appropriate exit status code", func() {
Expect(exitCode).To(Equal(0))
})
})

Context("Running a command which exits with non-zero status", func() {
BeforeEach(func() {
commandArgs := []string{"/usr/bin/false"}
configFilePath := createConfigFile("nocs-errcode.json", configFileDir, commandArgs)

cmd = exec.Command(nocsBin, "exec", configFilePath)
})

It("returns the appropriate exit status code", func() {
Expect(exitCode).To(Equal(1))
})
})

Context("Running without passing a configuration filepath", func() {
BeforeEach(func() {
commandArgs := []string{"echo", "Hello default OCF!"}
createConfigFile("config.json", configFileDir, commandArgs)

cmd = exec.Command(nocsBin, "exec")

nocsProcessWD = configFileDir
})

It("produces the appropriate stdout", func() {
Expect(stdout).To(Equal("Hello default OCF!\n"))
})
})

Context("Running when passing the empty configuration filepath", func() {
BeforeEach(func() {
commandArgs := []string{"echo", "Hello empty OCF!"}
createConfigFile("config.json", configFileDir, commandArgs)

cmd = exec.Command(nocsBin, "exec", "")

nocsProcessWD = configFileDir
})

It("produces the appropriate stdout", func() {
Expect(stdout).To(Equal("Hello empty OCF!\n"))
})
})

Context("Running with a missing configuration file", func() {
BeforeEach(func() {
cmd = exec.Command(nocsBin, "exec")

nocsProcessWD = configFileDir
})

It("produces the appropriate stderr", func() {
Expect(stderr).To(Equal(filepath.Base(nocsBin) + ": reading config file: open " + filepath.Join(configFileDir, "config.json: no such file or directory\n")))
})

It("returns the appropriate exit status code", func() {
Expect(exitCode).To(Equal(95))
})

})

Context("Running when passing a relative configuration filepath", func() {
BeforeEach(func() {
commandArgs := []string{"echo", "Hello relative OCF!"}
createConfigFile("relative.json", configFileDir, commandArgs)
subdirPath := filepath.Join(configFileDir, "some-subdir")
err := os.Mkdir(subdirPath, 0777)
Expect(err).NotTo(HaveOccurred())

cmd = exec.Command(nocsBin, "exec", "../relative.json")

nocsProcessWD = subdirPath
})

It("produces the appropriate stdout", func() {
Expect(stdout).To(Equal("Hello relative OCF!\n"))
})
})
})

func createConfigFile(configFileName, configFileDir string, args []string) string {
configFilePath := path.Join(configFileDir, configFileName)

err := ioutil.WriteFile(configFilePath, []byte(
`{"process": {
"user": {
"uid": 1,
"gid": 1
},
"args": `+argsToJSON(args)+`
}}`), 0777)
Expect(err).NotTo(HaveOccurred())

return configFilePath
}

func argsToJSON(args []string) string {
return `["` + strings.Join(args, `","`) + `"]`
}

func getExitCode(err error) int {
if err != nil {
exitErr, ok := err.(*exec.ExitError)
Expect(ok).To(BeTrue())

waitStatus, ok := exitErr.Sys().(syscall.WaitStatus)
Expect(ok).To(BeTrue())

return waitStatus.ExitStatus()
}
return 0
}

0 comments on commit d2b9e4c

Please sign in to comment.