| ... | ... |
@@ -2,9 +2,11 @@ package kubernetes |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"crypto/tls" |
| 5 |
+ "fmt" |
|
| 5 | 6 |
"net" |
| 6 | 7 |
"net/http" |
| 7 | 8 |
"os" |
| 9 |
+ "os/exec" |
|
| 8 | 10 |
"path/filepath" |
| 9 | 11 |
"reflect" |
| 10 | 12 |
"strconv" |
| ... | ... |
@@ -17,7 +19,7 @@ import ( |
| 17 | 17 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/proxy" |
| 18 | 18 |
pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config" |
| 19 | 19 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
| 20 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" |
|
| 20 |
+ kexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" |
|
| 21 | 21 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/iptables" |
| 22 | 22 |
|
| 23 | 23 |
"github.com/fsouza/go-dockerclient" |
| ... | ... |
@@ -36,6 +38,22 @@ const NodeScheme = "http" |
| 36 | 36 |
// NodePort is the default Kubelet port for serving information about the node. |
| 37 | 37 |
const NodePort = 10250 |
| 38 | 38 |
|
| 39 |
+type commandExecutor interface {
|
|
| 40 |
+ LookPath(executable string) (string, error) |
|
| 41 |
+ Run(command string, args ...string) error |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+type defaultCommandExecutor struct{}
|
|
| 45 |
+ |
|
| 46 |
+func (ce defaultCommandExecutor) LookPath(executable string) (string, error) {
|
|
| 47 |
+ return exec.LookPath(executable) |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func (ce defaultCommandExecutor) Run(command string, args ...string) error {
|
|
| 51 |
+ c := exec.Command(command, args...) |
|
| 52 |
+ return c.Run() |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 39 | 55 |
// NodeConfig represents the required parameters to start the OpenShift node |
| 40 | 56 |
// through Kubernetes. All fields are required. |
| 41 | 57 |
type NodeConfig struct {
|
| ... | ... |
@@ -80,17 +98,32 @@ func (c *NodeConfig) EnsureDocker(docker *dockerutil.Helper) {
|
| 80 | 80 |
// an absolute path and create the directory if it does not exist. Will exit if |
| 81 | 81 |
// an error is encountered. |
| 82 | 82 |
func (c *NodeConfig) EnsureVolumeDir() {
|
| 83 |
- rootDirectory, err := filepath.Abs(c.VolumeDir) |
|
| 83 |
+ if volumeDir, err := c.initializeVolumeDir(&defaultCommandExecutor{}, c.VolumeDir); err != nil {
|
|
| 84 |
+ glog.Fatal(err) |
|
| 85 |
+ } else {
|
|
| 86 |
+ c.VolumeDir = volumeDir |
|
| 87 |
+ } |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func (c *NodeConfig) initializeVolumeDir(ce commandExecutor, path string) (string, error) {
|
|
| 91 |
+ rootDirectory, err := filepath.Abs(path) |
|
| 84 | 92 |
if err != nil {
|
| 85 |
- glog.Fatalf("Error converting volume directory to an absolute path: %v", err)
|
|
| 93 |
+ return "", fmt.Errorf("Error converting volume directory to an absolute path: %v", err)
|
|
| 86 | 94 |
} |
| 87 | 95 |
|
| 88 | 96 |
if _, err := os.Stat(rootDirectory); os.IsNotExist(err) {
|
| 89 | 97 |
if mkdirErr := os.MkdirAll(rootDirectory, 0750); mkdirErr != nil {
|
| 90 |
- glog.Fatalf("Couldn't create kubelet volume root directory '%s': %s", rootDirectory, mkdirErr)
|
|
| 98 |
+ return "", fmt.Errorf("Couldn't create kubelet volume root directory '%s': %s", rootDirectory, mkdirErr)
|
|
| 99 |
+ } |
|
| 100 |
+ if chconPath, chconLookupErr := ce.LookPath("chcon"); chconLookupErr != nil {
|
|
| 101 |
+ glog.V(2).Infof("Couldn't locate 'chcon' to set the kubelet volume root directory context: %s", chconLookupErr)
|
|
| 102 |
+ } else {
|
|
| 103 |
+ if err := ce.Run(chconPath, "-t", "svirt_sandbox_file_t", rootDirectory); err != nil {
|
|
| 104 |
+ return "", fmt.Errorf("Error running 'chcon' to set the kubelet volume root directory context: %s", err)
|
|
| 105 |
+ } |
|
| 91 | 106 |
} |
| 92 | 107 |
} |
| 93 |
- c.VolumeDir = rootDirectory |
|
| 108 |
+ return rootDirectory, nil |
|
| 94 | 109 |
} |
| 95 | 110 |
|
| 96 | 111 |
// RunKubelet starts the Kubelet. |
| ... | ... |
@@ -192,7 +225,7 @@ func (c *NodeConfig) RunProxy() {
|
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 | 194 |
var proxier pconfig.ServiceConfigHandler |
| 195 |
- proxier = proxy.NewProxier(loadBalancer, ip, iptables.New(exec.New(), protocol)) |
|
| 195 |
+ proxier = proxy.NewProxier(loadBalancer, ip, iptables.New(kexec.New(), protocol)) |
|
| 196 | 196 |
if proxier == nil || reflect.ValueOf(proxier).IsNil() { // explicitly declared interfaces aren't plain nil, you must reflect inside to see if it's really nil or not
|
| 197 | 197 |
glog.Errorf("WARNING: Could not modify iptables. iptables must be mutable by this process to use services. Do you have root permissions?")
|
| 198 | 198 |
proxier = &service.FailingServiceConfigProxy{}
|
| 199 | 199 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,95 @@ |
| 0 |
+package kubernetes |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "io/ioutil" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+import "testing" |
|
| 10 |
+ |
|
| 11 |
+type fakeCommandExecutor struct {
|
|
| 12 |
+ commandFound bool |
|
| 13 |
+ commandErr error |
|
| 14 |
+ runCalled bool |
|
| 15 |
+ lookCalled bool |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func (f *fakeCommandExecutor) LookPath(path string) (string, error) {
|
|
| 19 |
+ f.lookCalled = true |
|
| 20 |
+ if f.commandFound {
|
|
| 21 |
+ return path, nil |
|
| 22 |
+ } |
|
| 23 |
+ return "", errors.New("not found")
|
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func (f *fakeCommandExecutor) Run(command string, args ...string) error {
|
|
| 27 |
+ f.runCalled = true |
|
| 28 |
+ return f.commandErr |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+func TestInitializeVolumeDir(t *testing.T) {
|
|
| 32 |
+ parentDir, err := ioutil.TempDir("", "")
|
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ t.Fatalf("Unable to create parent temp dir: %s", err)
|
|
| 35 |
+ } |
|
| 36 |
+ if err := os.MkdirAll(parentDir, 0750); err != nil {
|
|
| 37 |
+ t.Fatalf("Error creating volume parent dir: %s", err)
|
|
| 38 |
+ } |
|
| 39 |
+ defer os.RemoveAll(parentDir) |
|
| 40 |
+ |
|
| 41 |
+ testCases := []struct {
|
|
| 42 |
+ chconFound bool |
|
| 43 |
+ chconRunErr error |
|
| 44 |
+ removeVolumeDir bool |
|
| 45 |
+ }{
|
|
| 46 |
+ {chconFound: false, removeVolumeDir: true},
|
|
| 47 |
+ {chconFound: true, chconRunErr: nil, removeVolumeDir: true},
|
|
| 48 |
+ {chconFound: true, chconRunErr: errors.New("e"), removeVolumeDir: true},
|
|
| 49 |
+ {removeVolumeDir: false},
|
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ for i, testCase := range testCases {
|
|
| 53 |
+ ce := &fakeCommandExecutor{
|
|
| 54 |
+ commandFound: testCase.chconFound, |
|
| 55 |
+ commandErr: testCase.chconRunErr, |
|
| 56 |
+ } |
|
| 57 |
+ nc := &NodeConfig{VolumeDir: path.Join(parentDir, "somedir")}
|
|
| 58 |
+ |
|
| 59 |
+ if testCase.removeVolumeDir {
|
|
| 60 |
+ if err := os.RemoveAll(nc.VolumeDir); err != nil {
|
|
| 61 |
+ t.Fatalf("%d: Error removing volume dir: %s", i, err)
|
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ path, err := nc.initializeVolumeDir(ce, nc.VolumeDir) |
|
| 66 |
+ |
|
| 67 |
+ if testCase.removeVolumeDir {
|
|
| 68 |
+ if !ce.lookCalled {
|
|
| 69 |
+ t.Fatalf("%d: expected look for chcon", i)
|
|
| 70 |
+ } |
|
| 71 |
+ if !testCase.chconFound && ce.runCalled {
|
|
| 72 |
+ t.Fatalf("%d: unexpected run after chcon not found", i)
|
|
| 73 |
+ } |
|
| 74 |
+ if testCase.chconFound && !ce.runCalled {
|
|
| 75 |
+ t.Fatalf("%d: expected chcon run", i)
|
|
| 76 |
+ } |
|
| 77 |
+ haveErr := err != nil |
|
| 78 |
+ expectErr := testCase.chconRunErr != nil |
|
| 79 |
+ if expectErr != haveErr {
|
|
| 80 |
+ t.Fatalf("%d: expected chcon run err: %t, got: %t, %s", i, expectErr, haveErr, err)
|
|
| 81 |
+ } |
|
| 82 |
+ if !haveErr && path != nc.VolumeDir {
|
|
| 83 |
+ t.Fatalf("%d:, expected path(%s) == nc.VolumeDir(%s)", i, path, nc.VolumeDir)
|
|
| 84 |
+ } |
|
| 85 |
+ } else {
|
|
| 86 |
+ if ce.lookCalled {
|
|
| 87 |
+ t.Fatalf("%d: unexpected look for chcon with reused volume dir", i)
|
|
| 88 |
+ } |
|
| 89 |
+ if ce.runCalled {
|
|
| 90 |
+ t.Fatalf("%d: unexpected run for chcon with reused volume dir", i)
|
|
| 91 |
+ } |
|
| 92 |
+ } |
|
| 93 |
+ } |
|
| 94 |
+} |