github.com/john-lin/cni@v0.6.0-rc1.0.20170712150331-b69e640cc0e2/pkg/version/testhelpers/testhelpers.go (about)

     1  // Copyright 2016 CNI authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package testhelpers supports testing of CNI components of different versions
    16  //
    17  // For example, to build a plugin against an old version of the CNI library,
    18  // we can pass the plugin's source and the old git commit reference to BuildAt.
    19  // We could then test how the built binary responds when called by the latest
    20  // version of this library.
    21  package testhelpers
    22  
    23  import (
    24  	"fmt"
    25  	"io/ioutil"
    26  	"math/rand"
    27  	"os"
    28  	"os/exec"
    29  	"path/filepath"
    30  	"strings"
    31  	"time"
    32  )
    33  
    34  const packageBaseName = "github.com/containernetworking/cni"
    35  
    36  func run(cmd *exec.Cmd) error {
    37  	out, err := cmd.CombinedOutput()
    38  	if err != nil {
    39  		command := strings.Join(cmd.Args, " ")
    40  		return fmt.Errorf("running %q: %s", command, out)
    41  	}
    42  	return nil
    43  }
    44  
    45  func goBuildEnviron(gopath string) []string {
    46  	environ := os.Environ()
    47  	for i, kvp := range environ {
    48  		if strings.HasPrefix(kvp, "GOPATH=") {
    49  			environ[i] = "GOPATH=" + gopath
    50  			return environ
    51  		}
    52  	}
    53  	environ = append(environ, "GOPATH="+gopath)
    54  	return environ
    55  }
    56  
    57  func buildGoProgram(gopath, packageName, outputFilePath string) error {
    58  	cmd := exec.Command("go", "build", "-o", outputFilePath, packageName)
    59  	cmd.Env = goBuildEnviron(gopath)
    60  	return run(cmd)
    61  }
    62  
    63  func createSingleFilePackage(gopath, packageName string, fileContents []byte) error {
    64  	dirName := filepath.Join(gopath, "src", packageName)
    65  	err := os.MkdirAll(dirName, 0700)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	return ioutil.WriteFile(filepath.Join(dirName, "main.go"), fileContents, 0600)
    71  }
    72  
    73  func removePackage(gopath, packageName string) error {
    74  	dirName := filepath.Join(gopath, "src", packageName)
    75  	return os.RemoveAll(dirName)
    76  }
    77  
    78  func isRepoRoot(path string) bool {
    79  	_, err := ioutil.ReadDir(filepath.Join(path, ".git"))
    80  	return (err == nil) && (filepath.Base(path) == "cni")
    81  }
    82  
    83  func LocateCurrentGitRepo() (string, error) {
    84  	dir, err := os.Getwd()
    85  	if err != nil {
    86  		return "", err
    87  	}
    88  
    89  	for i := 0; i < 5; i++ {
    90  		if isRepoRoot(dir) {
    91  			return dir, nil
    92  		}
    93  
    94  		dir, err = filepath.Abs(filepath.Dir(dir))
    95  		if err != nil {
    96  			return "", fmt.Errorf("abs(dir(%q)): %s", dir, err)
    97  		}
    98  	}
    99  
   100  	return "", fmt.Errorf("unable to find cni repo root, landed at %q", dir)
   101  }
   102  
   103  func gitCloneThisRepo(cloneDestination string) error {
   104  	err := os.MkdirAll(cloneDestination, 0700)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	currentGitRepo, err := LocateCurrentGitRepo()
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	return run(exec.Command("git", "clone", currentGitRepo, cloneDestination))
   115  }
   116  
   117  func gitCheckout(localRepo string, gitRef string) error {
   118  	return run(exec.Command("git", "-C", localRepo, "checkout", gitRef))
   119  }
   120  
   121  // BuildAt builds the go programSource using the version of the CNI library
   122  // at gitRef, and saves the resulting binary file at outputFilePath
   123  func BuildAt(programSource []byte, gitRef string, outputFilePath string) error {
   124  	tempGoPath, err := ioutil.TempDir("", "cni-git-")
   125  	if err != nil {
   126  		return err
   127  	}
   128  	defer os.RemoveAll(tempGoPath)
   129  
   130  	cloneDestination := filepath.Join(tempGoPath, "src", packageBaseName)
   131  	err = gitCloneThisRepo(cloneDestination)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	err = gitCheckout(cloneDestination, gitRef)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	rand.Seed(time.Now().UnixNano())
   142  	testPackageName := fmt.Sprintf("test-package-%x", rand.Int31())
   143  
   144  	err = createSingleFilePackage(tempGoPath, testPackageName, programSource)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	defer removePackage(tempGoPath, testPackageName)
   149  
   150  	err = buildGoProgram(tempGoPath, testPackageName, outputFilePath)
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	return nil
   156  }