Add support for git volumes.

pull/6/head
Brendan Burns 2014-10-21 16:23:05 -07:00
parent 87c77e5ba7
commit b59e49fa13
9 changed files with 394 additions and 180 deletions

View File

@ -147,8 +147,8 @@ type VolumeSource struct {
// GCEPersistentDisk represents a GCE Disk resource that is attached to a
// kubelet's host machine and then exposed to the pod.
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk" yaml:"persistentDisk"`
// GitVolume represents a git repository at a particular revision.
GitVolume *GitVolume `json:"gitVolume" yaml:"gitVolume"`
// GitRepo represents a git repository at a particular revision.
GitRepo *GitRepo `json:"gitRepo" yaml:"gitRepo"`
}
// HostDir represents bare host directory volume.
@ -189,9 +189,13 @@ type GCEPersistentDisk struct {
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
}
type GitVolume struct {
// GitRepo represents a volume that is pulled from git when the pod is created.
type GitRepo struct {
// Repository URL
Repository string
Repository string `yaml:"repository" json:"repository"`
// Commit hash, this is optional
Revision string `yaml:"revision" json:"revision"`
// TODO: Consider credentials here.
}
// Port represents a network port in a single container

View File

@ -93,6 +93,8 @@ type VolumeSource struct {
// GCEPersistentDisk represents a GCE Disk resource that is attached to a
// kubelet's host machine and then exposed to the pod.
GCEPersistentDisk *GCEPersistentDisk `yaml:"persistentDisk" json:"persistentDisk"`
// GitRepo represents a git repository at a particular revision.
GitRepo *GitRepo `json:"gitRepo" yaml:"gitRepo"`
}
// HostDir represents bare host directory volume.
@ -133,6 +135,14 @@ type GCEPersistentDisk struct {
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
}
// GitRepo represents a volume that is pulled from git when the pod is created.
type GitRepo struct {
// Repository URL
Repository string `yaml:"repository" json:"repository"`
// Commit hash, this is optional
Revision string `yaml:"revision" json:"revision"`
}
// Port represents a network port in a single container
type Port struct {
// Optional: If specified, this must be a DNS_LABEL. Each named port

View File

@ -69,6 +69,8 @@ type VolumeSource struct {
// A persistent disk that is mounted to the
// kubelet's host machine and then exposed to the pod.
GCEPersistentDisk *GCEPersistentDisk `yaml:"persistentDisk" json:"persistentDisk"`
// GitRepo represents a git repository at a particular revision.
GitRepo *GitRepo `json:"gitRepo" yaml:"gitRepo"`
}
// HostDir represents bare host directory volume.
@ -124,6 +126,14 @@ type GCEPersistentDisk struct {
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
}
// GitRepo represents a volume that is pulled from git when the pod is created.
type GitRepo struct {
// Repository URL
Repository string `yaml:"repository" json:"repository"`
// Commit hash, this is optional
Revision string `yaml:"revision" json:"revision"`
}
// VolumeMount describes a mounting of a Volume within a container.
type VolumeMount struct {
// Required: This must match the Name of a Volume [above].

View File

@ -178,6 +178,8 @@ type VolumeSource struct {
// GCEPersistentDisk represents a GCE Disk resource that is attached to a
// kubelet's host machine and then exposed to the pod.
GCEPersistentDisk *GCEPersistentDisk `yaml:"persistentDisk" json:"persistentDisk"`
// GitRepo represents a git repository at a particular revision.
GitRepo *GitRepo `json:"gitRepo" yaml:"gitRepo"`
}
// HostDir represents bare host directory volume.
@ -218,6 +220,14 @@ type GCEPersistentDisk struct {
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
}
// GitRepo represents a volume that is pulled from git when the pod is created.
type GitRepo struct {
// Repository URL
Repository string `yaml:"repository" json:"repository"`
// Commit hash, this is optional
Revision string `yaml:"revision" json:"revision"`
}
// Port represents a network port in a single container.
type Port struct {
// Optional: If specified, this must be a DNS_LABEL. Each named port

View File

@ -36,6 +36,7 @@ type Cmd interface {
// CombinedOutput runs the command and returns its combined standard output
// and standard error. This follows the pattern of package os/exec.
CombinedOutput() ([]byte, error)
SetDir(dir string)
}
// ExitError is an interface that presents an API similar to os.ProcessState, which is
@ -64,6 +65,10 @@ func (executor *executor) Command(cmd string, args ...string) Cmd {
// Wraps exec.Cmd so we can capture errors.
type cmdWrapper osexec.Cmd
func (cmd *cmdWrapper) SetDir(dir string) {
cmd.Dir = dir
}
// CombinedOutput is part of the Cmd interface.
func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) {
out, err := (*osexec.Cmd)(cmd).CombinedOutput()

View File

@ -0,0 +1,92 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec
import (
"fmt"
)
// A simple scripted Interface type.
type FakeExec struct {
CommandScript []FakeCommandAction
CommandCalls int
}
type FakeCommandAction func(cmd string, args ...string) Cmd
func (fake *FakeExec) Command(cmd string, args ...string) Cmd {
if fake.CommandCalls > len(fake.CommandScript)-1 {
panic("ran out of Command() actions")
}
i := fake.CommandCalls
fake.CommandCalls++
return fake.CommandScript[i](cmd, args...)
}
// A simple scripted Cmd type.
type FakeCmd struct {
Argv []string
CombinedOutputScript []FakeCombinedOutputAction
CombinedOutputCalls int
CombinedOutputLog [][]string
Dirs []string
}
func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) Cmd {
fake.Argv = append([]string{cmd}, args...)
return fake
}
type FakeCombinedOutputAction func() ([]byte, error)
func (fake *FakeCmd) SetDir(dir string) {
fake.Dirs = append(fake.Dirs, dir)
}
func (fake *FakeCmd) CombinedOutput() ([]byte, error) {
if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 {
panic("ran out of CombinedOutput() actions")
}
if fake.CombinedOutputLog == nil {
fake.CombinedOutputLog = [][]string{}
}
i := fake.CombinedOutputCalls
fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...))
fake.CombinedOutputCalls++
return fake.CombinedOutputScript[i]()
}
// A simple fake ExitError type.
type FakeExitError struct {
Status int
}
func (fake *FakeExitError) String() string {
return fmt.Sprintf("exit %d", fake.Status)
}
func (fake *FakeExitError) Error() string {
return fake.String()
}
func (fake *FakeExitError) Exited() bool {
return true
}
func (fake *FakeExitError) ExitStatus() int {
return fake.Status
}

View File

@ -17,95 +17,28 @@ limitations under the License.
package iptables
import (
"fmt"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
utilexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
)
// A simple scripted utilexec.Interface type.
type fakeExec struct {
commandScript []fakeCommandAction
commandCalls int
}
type fakeCommandAction func(cmd string, args ...string) utilexec.Cmd
func (fake *fakeExec) Command(cmd string, args ...string) utilexec.Cmd {
if fake.commandCalls > len(fake.commandScript)-1 {
panic("ran out of Command() actions")
}
i := fake.commandCalls
fake.commandCalls++
return fake.commandScript[i](cmd, args...)
}
// A simple scripted utilexec.Cmd type.
type fakeCmd struct {
argv []string
combinedOutputScript []fakeCombinedOutputAction
combinedOutputCalls int
combinedOutputLog [][]string
}
func initFakeCmd(fake *fakeCmd, cmd string, args ...string) utilexec.Cmd {
fake.argv = append([]string{cmd}, args...)
return fake
}
type fakeCombinedOutputAction func() ([]byte, error)
func (fake *fakeCmd) CombinedOutput() ([]byte, error) {
if fake.combinedOutputCalls > len(fake.combinedOutputScript)-1 {
panic("ran out of CombinedOutput() actions")
}
if fake.combinedOutputLog == nil {
fake.combinedOutputLog = [][]string{}
}
i := fake.combinedOutputCalls
fake.combinedOutputLog = append(fake.combinedOutputLog, append([]string{}, fake.argv...))
fake.combinedOutputCalls++
return fake.combinedOutputScript[i]()
}
// A simple fake utilexec.ExitError type.
type fakeExitError struct {
status int
}
func (fake *fakeExitError) String() string {
return fmt.Sprintf("exit %d", fake.status)
}
func (fake *fakeExitError) Error() string {
return fake.String()
}
func (fake *fakeExitError) Exited() bool {
return true
}
func (fake *fakeExitError) ExitStatus() int {
return fake.status
}
func TestEnsureChain(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Success.
func() ([]byte, error) { return []byte{}, nil },
// Exists.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
// Failure.
func() ([]byte, error) { return nil, &fakeExitError{2} },
func() ([]byte, error) { return nil, &exec.FakeExitError{2} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -117,11 +50,11 @@ func TestEnsureChain(t *testing.T) {
if exists {
t.Errorf("expected exists = false")
}
if fcmd.combinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
}
if !util.NewStringSet(fcmd.combinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.combinedOutputLog[0])
if !util.NewStringSet(fcmd.CombinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Exists.
exists, err = runner.EnsureChain(TableNAT, Chain("FOOBAR"))
@ -139,18 +72,18 @@ func TestEnsureChain(t *testing.T) {
}
func TestFlushChain(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Success.
func() ([]byte, error) { return []byte{}, nil },
// Failure.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -159,11 +92,11 @@ func TestFlushChain(t *testing.T) {
if err != nil {
t.Errorf("expected success, got %+v", err)
}
if fcmd.combinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
}
if !util.NewStringSet(fcmd.combinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.combinedOutputLog[0])
if !util.NewStringSet(fcmd.CombinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Failure.
err = runner.FlushChain(TableNAT, Chain("FOOBAR"))
@ -173,16 +106,16 @@ func TestFlushChain(t *testing.T) {
}
func TestEnsureRuleAlreadyExists(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Success.
func() ([]byte, error) { return []byte{}, nil },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Success of that exec means "done".
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -193,28 +126,28 @@ func TestEnsureRuleAlreadyExists(t *testing.T) {
if !exists {
t.Errorf("expected exists = true")
}
if fcmd.combinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
}
if !util.NewStringSet(fcmd.combinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.combinedOutputLog[0])
if !util.NewStringSet(fcmd.CombinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
}
func TestEnsureRuleNew(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Status 1 on the first call.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
// Success on the second call.
func() ([]byte, error) { return []byte{}, nil },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Failure of that means create it.
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -225,25 +158,25 @@ func TestEnsureRuleNew(t *testing.T) {
if exists {
t.Errorf("expected exists = false")
}
if fcmd.combinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !util.NewStringSet(fcmd.combinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.combinedOutputLog[1])
if !util.NewStringSet(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
}
}
func TestEnsureRuleErrorChecking(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Status 2 on the first call.
func() ([]byte, error) { return nil, &fakeExitError{2} },
func() ([]byte, error) { return nil, &exec.FakeExitError{2} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Failure of that means create it.
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -251,25 +184,25 @@ func TestEnsureRuleErrorChecking(t *testing.T) {
if err == nil {
t.Errorf("expected failure")
}
if fcmd.combinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
}
}
func TestEnsureRuleErrorCreating(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Status 1 on the first call.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
// Status 1 on the second call.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Failure of that means create it.
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -277,22 +210,22 @@ func TestEnsureRuleErrorCreating(t *testing.T) {
if err == nil {
t.Errorf("expected failure")
}
if fcmd.combinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
}
func TestDeleteRuleAlreadyExists(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Status 1 on the first call.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Failure of that exec means "does not exist".
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -300,28 +233,28 @@ func TestDeleteRuleAlreadyExists(t *testing.T) {
if err != nil {
t.Errorf("expected success, got %+v", err)
}
if fcmd.combinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
}
if !util.NewStringSet(fcmd.combinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.combinedOutputLog[0])
if !util.NewStringSet(fcmd.CombinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
}
func TestDeleteRuleNew(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Success on the first call.
func() ([]byte, error) { return []byte{}, nil },
// Success on the second call.
func() ([]byte, error) { return []byte{}, nil },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Success of that means delete it.
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -329,25 +262,25 @@ func TestDeleteRuleNew(t *testing.T) {
if err != nil {
t.Errorf("expected success, got %+v", err)
}
if fcmd.combinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !util.NewStringSet(fcmd.combinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.combinedOutputLog[1])
if !util.NewStringSet(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
}
}
func TestDeleteRuleErrorChecking(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Status 2 on the first call.
func() ([]byte, error) { return nil, &fakeExitError{2} },
func() ([]byte, error) { return nil, &exec.FakeExitError{2} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Failure of that means create it.
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -355,25 +288,25 @@ func TestDeleteRuleErrorChecking(t *testing.T) {
if err == nil {
t.Errorf("expected failure")
}
if fcmd.combinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
}
}
func TestDeleteRuleErrorCreating(t *testing.T) {
fcmd := fakeCmd{
combinedOutputScript: []fakeCombinedOutputAction{
fcmd := exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// Success on the first call.
func() ([]byte, error) { return []byte{}, nil },
// Status 1 on the second call.
func() ([]byte, error) { return nil, &fakeExitError{1} },
func() ([]byte, error) { return nil, &exec.FakeExitError{1} },
},
}
fexec := fakeExec{
commandScript: []fakeCommandAction{
fexec := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
// The first Command() call is checking the rule. Success of that means delete it.
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) utilexec.Cmd { return initFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
@ -381,7 +314,7 @@ func TestDeleteRuleErrorCreating(t *testing.T) {
if err == nil {
t.Errorf("expected failure")
}
if fcmd.combinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.combinedOutputCalls)
if fcmd.CombinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
}

View File

@ -18,12 +18,14 @@ package volume
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
"github.com/golang/glog"
)
@ -82,6 +84,82 @@ func (hostVol *HostDir) GetPath() string {
return hostVol.Path
}
type execInterface interface {
ExecCommand(cmd []string, dir string) ([]byte, error)
}
type GitDir struct {
Source string
Revision string
PodID string
RootDir string
Name string
exec exec.Interface
}
func newGitRepo(volume *api.Volume, podID, rootDir string) *GitDir {
return &GitDir{
Source: volume.Source.GitRepo.Repository,
Revision: volume.Source.GitRepo.Revision,
PodID: podID,
RootDir: rootDir,
Name: volume.Name,
exec: exec.New(),
}
}
func (g *GitDir) ExecCommand(command string, args []string, dir string) ([]byte, error) {
cmd := g.exec.Command(command, args...)
cmd.SetDir(dir)
return cmd.CombinedOutput()
}
func (g *GitDir) SetUp() error {
volumePath := g.GetPath()
if err := os.MkdirAll(volumePath, 0750); err != nil {
return err
}
if _, err := g.ExecCommand("git", []string{"clone", g.Source}, g.GetPath()); err != nil {
return err
}
files, err := ioutil.ReadDir(g.GetPath())
if err != nil {
return err
}
if len(g.Revision) == 0 {
return nil
}
if len(files) != 1 {
return fmt.Errorf("Unexpected directory contents: %v", files)
}
dir := path.Join(g.GetPath(), files[0].Name())
if _, err := g.ExecCommand("git", []string{"checkout", g.Revision}, dir); err != nil {
return err
}
if _, err := g.ExecCommand("git", []string{"reset", "--hard"}, dir); err != nil {
return err
}
return nil
}
func (g *GitDir) GetPath() string {
return path.Join(g.RootDir, g.PodID, "volumes", "git", g.Name)
}
// TearDown simply deletes everything in the directory.
func (g *GitDir) TearDown() error {
tmpDir, err := renameDirectory(g.GetPath(), g.Name+"~deleting")
if err != nil {
return err
}
err = os.RemoveAll(tmpDir)
if err != nil {
return err
}
return nil
}
// EmptyDir volumes are temporary directories exposed to the pod.
// These do not persist beyond the lifetime of a pod.
type EmptyDir struct {
@ -93,20 +171,15 @@ type EmptyDir struct {
// SetUp creates new directory.
func (emptyDir *EmptyDir) SetUp() error {
path := emptyDir.GetPath()
err := os.MkdirAll(path, 0750)
if err != nil {
return err
}
return nil
return os.MkdirAll(path, 0750)
}
func (emptyDir *EmptyDir) GetPath() string {
return path.Join(emptyDir.RootDir, emptyDir.PodID, "volumes", "empty", emptyDir.Name)
}
func (emptyDir *EmptyDir) renameDirectory() (string, error) {
oldPath := emptyDir.GetPath()
newPath, err := ioutil.TempDir(path.Dir(oldPath), emptyDir.Name+".deleting~")
func renameDirectory(oldPath, newName string) (string, error) {
newPath, err := ioutil.TempDir(path.Dir(oldPath), newName)
if err != nil {
return "", err
}
@ -119,7 +192,7 @@ func (emptyDir *EmptyDir) renameDirectory() (string, error) {
// TearDown simply deletes everything in the directory.
func (emptyDir *EmptyDir) TearDown() error {
tmpDir, err := emptyDir.renameDirectory()
tmpDir, err := renameDirectory(emptyDir.GetPath(), emptyDir.Name+".deleting~")
if err != nil {
return err
}
@ -279,6 +352,8 @@ func CreateVolumeBuilder(volume *api.Volume, podID string, rootDir string) (Buil
if err != nil {
return nil, err
}
} else if source.GitRepo != nil {
vol = newGitRepo(volume, podID, rootDir)
} else {
return nil, ErrUnsupportedVolumeType
}
@ -297,6 +372,12 @@ func CreateVolumeCleaner(kind string, name string, podID string, rootDir string)
RootDir: rootDir,
util: &GCEDiskUtil{},
mounter: &DiskMounter{}}, nil
case "git":
return &GitDir{
Name: name,
PodID: podID,
RootDir: rootDir,
}, nil
default:
return nil, ErrUnsupportedVolumeType
}

View File

@ -24,6 +24,7 @@ import (
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
)
type MockDiskUtil struct{}
@ -224,3 +225,71 @@ func TestGetActiveVolumes(t *testing.T) {
}
}
}
type fakeExec struct {
cmds [][]string
dirs []string
data []byte
err error
action func([]string, string)
}
func (f *fakeExec) ExecCommand(cmd []string, dir string) ([]byte, error) {
f.cmds = append(f.cmds, cmd)
f.dirs = append(f.dirs, dir)
f.action(cmd, dir)
return f.data, f.err
}
func TestGitVolume(t *testing.T) {
var fcmd exec.FakeCmd
fcmd = exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
func() ([]byte, error) {
os.MkdirAll(path.Join(fcmd.Dirs[0], "kubernetes"), 0750)
return []byte{}, nil
},
func() ([]byte, error) { return []byte{}, nil },
func() ([]byte, error) { return []byte{}, nil },
},
}
fake := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
dir := os.TempDir() + "/git"
g := GitDir{
Source: "https://github.com/GoogleCloudPlatform/kubernetes.git",
Revision: "2a30ce65c5ab586b98916d83385c5983edd353a1",
PodID: "foo",
RootDir: dir,
Name: "test-pod",
exec: &fake,
}
err := g.SetUp()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
expectedCmds := [][]string{
{"git", "clone", g.Source},
{"git", "checkout", g.Revision},
{"git", "reset", "--hard"},
}
if fake.CommandCalls != len(expectedCmds) {
t.Errorf("unexpected command calls: expected 3, saw: %d", fake.CommandCalls)
}
if !reflect.DeepEqual(expectedCmds, fcmd.CombinedOutputLog) {
t.Errorf("unexpected commands: %v, expected: %v", fcmd.CombinedOutputLog, expectedCmds)
}
expectedDirs := []string{g.GetPath(), g.GetPath() + "/kubernetes", g.GetPath() + "/kubernetes"}
if len(fcmd.Dirs) != 3 || !reflect.DeepEqual(expectedDirs, fcmd.Dirs) {
t.Errorf("unexpected directories: %v, expected: %v", fcmd.Dirs, expectedDirs)
}
err = g.TearDown()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}