diff --git a/Makefile b/Makefile index baea95fe..d1677b8b 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ test: go test `go list ./... | grep -v -e 'vendor' -e 'test'` $(TESTARGS) go vet `go list ./... | grep -v vendor` -e2e: KUBECONFIG?=$(HOME)/.kube/config +e2e: KUBECONFIG?=$(HOME)/.kube/config e2e: - ./hack/e2e.sh $(IMAGE_NAME):$(TAG) $(KUBECONFIG) + ./hack/e2e.sh $(IMAGE_NAME):$(TAG) $(KUBECONFIG) ./bin/genectl diff --git a/cmd/genectl/parser/parser.go b/cmd/genectl/parser/parser.go index f621a9bc..6f2d4271 100644 --- a/cmd/genectl/parser/parser.go +++ b/cmd/genectl/parser/parser.go @@ -253,17 +253,26 @@ func TransWorkflow2Execution(workflow *Workflow) (*execv1alpha1.Execution, error // we have alreay merge workflows command and commandIter. task.CommandSet = jobInfo.Commands - // parse Res - cpuNum := strings.TrimRight(jobInfo.Resources.Cpu, "cC") - cpuQuantity, err := resource.ParseQuantity(cpuNum) - if err != nil { - return nil, fmt.Errorf("parse Res quantity error: %v", err) + var cpuQuantity resource.Quantity + var memoryQuantity resource.Quantity + var err error + + // parse cpu + if len(jobInfo.Resources.Cpu) > 0 { + cpuNum := strings.TrimRight(jobInfo.Resources.Cpu, "cC") + cpuQuantity, err = resource.ParseQuantity(cpuNum) + if err != nil { + return nil, fmt.Errorf("parse cpu quantity error: %v", err) + } } // parse memory - memoryQuantity, err := resource.ParseQuantity(jobInfo.Resources.Memory) - if err != nil { - return nil, fmt.Errorf("parse mem quantity error: %v", err) + if len(jobInfo.Resources.Memory) > 0 { + memoryQuantity, err = resource.ParseQuantity(jobInfo.Resources.Memory) + if err != nil { + return nil, fmt.Errorf("parse mem quantity error: %v", err) + } } + task.Resources = execv1alpha1.ResourceRequirements{ Cpu: cpuQuantity, Memory: memoryQuantity, diff --git a/hack/e2e.sh b/hack/e2e.sh index b48c6629..67bd2894 100755 --- a/hack/e2e.sh +++ b/hack/e2e.sh @@ -28,7 +28,7 @@ cp gene2e ./hack/ unset http_proxy unset https_proxy -./gene2e --image=$1 --kubeconfig=$2 +./gene2e --image=$1 --kubeconfig=$2 --genectl=$3 rm gene2e diff --git a/test/e2e/genectl.go b/test/e2e/genectl.go new file mode 100644 index 00000000..be4a8381 --- /dev/null +++ b/test/e2e/genectl.go @@ -0,0 +1,122 @@ +/* +Copyright 2018 The Kubegene Authors. + +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 e2e + +import ( + "os" + "os/exec" + "path/filepath" + + "github.com/golang/glog" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/kubernetes" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var GOPATH = os.Getenv("GOPATH") + +var ToolRepo = filepath.Join(GOPATH, "src/kubegene.io/kubegene/example/tools") + +var _ = DescribeGene("genectl", func(gtc *GeneTestContext) { + var ns string + var kubeClient kubernetes.Interface + + BeforeEach(func() { + ns = gtc.Config.Namespace + kubeClient = gtc.KubeClient + }) + + AfterEach(func() { + }) + + It("sub single job", func() { + By("Make dir for test") + err := execCommand("mkdir", []string{"-p", "/tmp/subjob"}) + Expect(err).NotTo(HaveOccurred()) + + By("Prepare shell script") + err = execCommand("cp", []string{"example/single-job/print.sh", "/tmp/subjob/"}) + Expect(err).NotTo(HaveOccurred()) + + createVolumeAndClaim("example/single-job/subjob-pv.yaml", "example/single-job/subjob-pvc.yaml", ns, kubeClient) + + By("Execute sub job command") + cmd := NewGenectlCommand("sub", "job", "/tmp/subjob/print.sh", "--tool=nginx:latest", "--pvc=subjob-pvc", "--tool-repo="+ToolRepo) + output := cmd.ExecOrDie() + glog.Infof("output: %v", output) + }) + + It("sub group job", func() { + By("Make dir for test") + err := execCommand("mkdir", []string{"-p", "/tmp/subrepjob"}) + Expect(err).NotTo(HaveOccurred()) + + By("Prepare shell script") + err = execCommand("cp", []string{"example/group-job/print-date.sh", "/tmp/subrepjob/"}) + Expect(err).NotTo(HaveOccurred()) + err = execCommand("cp", []string{"example/group-job/print-number.sh", "/tmp/subrepjob/"}) + Expect(err).NotTo(HaveOccurred()) + err = execCommand("cp", []string{"example/group-job/repjob.sh", "/tmp/subrepjob/"}) + Expect(err).NotTo(HaveOccurred()) + + createVolumeAndClaim("example/group-job/subrepjob-pv.yaml", "example/group-job/subrepjob-pvc.yaml", ns, kubeClient) + + By("Execute sub repjob command") + cmd := NewGenectlCommand("sub", "repjob", "/tmp/subrepjob/repjob.sh", "--tool=nginx:latest", "--pvc=subrepjob-pvc", "--tool-repo="+ToolRepo) + output := cmd.ExecOrDie() + glog.Infof("output: %v", output) + }) + + It("sub workflow", func() { + createVolumeAndClaim("example/simple-sample/sample-pv.yaml", "example/simple-sample/sample-pvc.yaml", ns, kubeClient) + + By("Execute sub workflow command") + cmd := NewGenectlCommand("sub", "workflow", "example/simple-sample/simple-sample.yaml", "--tool-repo="+ToolRepo) + output := cmd.ExecOrDie() + glog.Infof("output: %v", output) + }) +}) + +func createVolumeAndClaim(volumeFile, claimFile, ns string, kubeClient kubernetes.Interface) { + By("Create a volume") + volume, err := volumeFromManifest(volumeFile) + Expect(err).NotTo(HaveOccurred()) + _, err = kubeClient.CoreV1().PersistentVolumes().Create(volume) + Expect(err == nil || errors.IsAlreadyExists(err)).To(Equal(true)) + + By("Create a claim") + claim, err := claimFromManifest(claimFile) + Expect(err).NotTo(HaveOccurred()) + claim.Namespace = ns + _, err = kubeClient.CoreV1().PersistentVolumeClaims(ns).Create(claim) + Expect(err == nil || errors.IsAlreadyExists(err)).To(Equal(true)) + + err = WaitForPersistentVolumeBound(kubeClient, volume.Name) + Expect(err).NotTo(HaveOccurred()) + + err = WaitForPersistentVolumeClaimBound(kubeClient, ns, claim.Name) + Expect(err).NotTo(HaveOccurred()) +} + +func execCommand(name string, args []string) error { + cmd := exec.Command(name, args...) + output, err := cmd.CombinedOutput() + glog.Infof("command: %v, output: %v", name, string(output)) + return err +} diff --git a/test/e2e/genectlWrapper.go b/test/e2e/genectlWrapper.go new file mode 100644 index 00000000..342df75c --- /dev/null +++ b/test/e2e/genectlWrapper.go @@ -0,0 +1,94 @@ +/* +Copyright 2018 The Kubegene Authors. + +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 e2e + +import ( + "bytes" + "fmt" + "os/exec" + "strings" + "syscall" + "time" + + "github.com/golang/glog" + . "github.com/onsi/gomega" +) + +type genectlWrapper struct { + cmd *exec.Cmd + timeout <-chan time.Time +} + +func NewGenectlCommand(args ...string) *genectlWrapper { + ctlWrapper := new(genectlWrapper) + ctlWrapper.cmd = GenectlCmd(args...) + return ctlWrapper +} + +// GenectlCmd runs the genectl executable through the wrapper script. +func GenectlCmd(args ...string) *exec.Cmd { + defaultArgs := []string{} + + if geneTestContext.Config.KubeConfig != "" { + defaultArgs = append(defaultArgs, "--kubeconfig="+geneTestContext.Config.KubeConfig) + } + genectlArgs := append(defaultArgs, args...) + + //We allow users to specify path to genectl. + cmd := exec.Command(geneTestContext.Config.GenectlPath, genectlArgs...) + + //caller will invoke this and wait on it. + return cmd +} + +func (ctlWrapper genectlWrapper) ExecOrDie() string { + str, err := ctlWrapper.Exec() + Expect(err).NotTo(HaveOccurred()) + return str +} + +func (ctlWrapper genectlWrapper) Exec() (string, error) { + var stdout, stderr bytes.Buffer + cmd := ctlWrapper.cmd + cmd.Stdout, cmd.Stderr = &stdout, &stderr + + glog.Infof("Running '%s %s'", cmd.Path, strings.Join(cmd.Args, " ")) + if err := cmd.Start(); err != nil { + return "", fmt.Errorf("error starting %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v\n", cmd, cmd.Stdout, cmd.Stderr, err) + } + errCh := make(chan error, 1) + go func() { + errCh <- cmd.Wait() + }() + select { + case err := <-errCh: + if err != nil { + var rc int = 127 + if ee, ok := err.(*exec.ExitError); ok { + rc = int(ee.Sys().(syscall.WaitStatus).ExitStatus()) + glog.Infof("rc: %d", rc) + } + return "", fmt.Errorf("error running %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v\n", cmd, cmd.Stdout, cmd.Stderr, err) + } + case <-ctlWrapper.timeout: + ctlWrapper.cmd.Process.Kill() + return "", fmt.Errorf("timed out waiting for command %v:\nCommand stdout:\n%v\nstderr:\n%v\n", cmd, cmd.Stdout, cmd.Stderr) + } + glog.Infof("stderr: %q", stderr.String()) + glog.Infof("stdout: %q", stdout.String()) + return stdout.String(), nil +} diff --git a/test/e2e/helper.go b/test/e2e/helper.go index 7d0e4b6a..4620e4d5 100644 --- a/test/e2e/helper.go +++ b/test/e2e/helper.go @@ -269,3 +269,33 @@ func readFile(path string) ([]byte, error) { data, err := ioutil.ReadFile(absPath) return data, err } + +func volumeFromManifest(fileName string) (*v1.PersistentVolume, error) { + data, err := readFile(fileName) + if err != nil { + return nil, err + } + + var persistentVolume v1.PersistentVolume + err = yaml.Unmarshal(data, &persistentVolume) + if err != nil { + return nil, err + } + + return &persistentVolume, nil +} + +func claimFromManifest(fileName string) (*v1.PersistentVolumeClaim, error) { + data, err := readFile(fileName) + if err != nil { + return nil, err + } + + var claim v1.PersistentVolumeClaim + err = yaml.Unmarshal(data, &claim) + if err != nil { + return nil, err + } + + return &claim, nil +} diff --git a/test/e2e/e2e.go b/test/e2e/kubedag.go similarity index 100% rename from test/e2e/e2e.go rename to test/e2e/kubedag.go diff --git a/test/e2e/test_context.go b/test/e2e/test_context.go index 5fe66b96..36cc56b0 100644 --- a/test/e2e/test_context.go +++ b/test/e2e/test_context.go @@ -29,13 +29,14 @@ import ( . "github.com/onsi/gomega" ) -var gtc *GeneTestContext +var geneTestContext *GeneTestContext // Config provides the configuration for the e2e tests. type Config struct { KubeConfig string KubeDagImage string Namespace string + GenectlPath string } // GeneTestContext holds the variables that each test can depend on. It @@ -47,21 +48,21 @@ type GeneTestContext struct { } func Test(t *testing.T, config *Config) { - gtc = &GeneTestContext{ + geneTestContext = &GeneTestContext{ Config: config, } - registerTestsInGinkgo(gtc) + registerTestsInGinkgo(geneTestContext) RegisterFailHandler(Fail) RunSpecs(t, "Kubegene Test Suite") } var _ = BeforeSuite(func() { - gtc.setup() + geneTestContext.setup() }) var _ = AfterSuite(func() { - gtc.teardown() + geneTestContext.teardown() }) func (gtc *GeneTestContext) setup() { diff --git a/test/e2e_test.go b/test/e2e_test.go index 642ef354..13056cce 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -32,6 +32,7 @@ const Kubegene = "kubegene" func init() { flag.StringVar(&config.KubeConfig, "kubeconfig", "", "The kube config path") + flag.StringVar(&config.GenectlPath, "genectl", "", "The path of genectl binary") flag.StringVar(&config.KubeDagImage, "image", "", "The kubedag image") flag.StringVar(&config.Namespace, "namespace", "", "Namespace to run the test") flag.Set("logtostderr", "true")