github.com/arunkumar7540/cli@v6.45.0+incompatible/actor/v7action/buildpack.go (about)

     1  package v7action
     2  
     3  import (
     4  	"archive/zip"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"code.cloudfoundry.org/cli/actor/actionerror"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    12  	"code.cloudfoundry.org/cli/util"
    13  )
    14  
    15  type Buildpack ccv3.Buildpack
    16  type JobURL ccv3.JobURL
    17  
    18  //go:generate counterfeiter . Downloader
    19  
    20  type Downloader interface {
    21  	Download(url string, tmpDirPath string) (string, error)
    22  }
    23  
    24  func (actor Actor) GetBuildpacks() ([]Buildpack, Warnings, error) {
    25  	ccv3Buildpacks, warnings, err := actor.CloudControllerClient.GetBuildpacks(ccv3.Query{
    26  		Key:    ccv3.OrderBy,
    27  		Values: []string{ccv3.PositionOrder},
    28  	})
    29  
    30  	var buildpacks []Buildpack
    31  	for _, buildpack := range ccv3Buildpacks {
    32  		buildpacks = append(buildpacks, Buildpack(buildpack))
    33  	}
    34  
    35  	return buildpacks, Warnings(warnings), err
    36  }
    37  
    38  // GetBuildpackByNameAndStack returns a buildpack with the provided name and
    39  // stack. If `buildpackStack` is not specified, and there are multiple
    40  // buildpacks with the same name, it will return the one with no stack, if
    41  // present.
    42  func (actor Actor) GetBuildpackByNameAndStack(buildpackName string, buildpackStack string) (Buildpack, Warnings, error) {
    43  	var (
    44  		ccv3Buildpacks []ccv3.Buildpack
    45  		warnings       ccv3.Warnings
    46  		err            error
    47  	)
    48  
    49  	if buildpackStack == "" {
    50  		ccv3Buildpacks, warnings, err = actor.CloudControllerClient.GetBuildpacks(ccv3.Query{
    51  			Key:    ccv3.NameFilter,
    52  			Values: []string{buildpackName},
    53  		})
    54  	} else {
    55  		ccv3Buildpacks, warnings, err = actor.CloudControllerClient.GetBuildpacks(
    56  			ccv3.Query{
    57  				Key:    ccv3.NameFilter,
    58  				Values: []string{buildpackName},
    59  			},
    60  			ccv3.Query{
    61  				Key:    ccv3.StackFilter,
    62  				Values: []string{buildpackStack},
    63  			},
    64  		)
    65  	}
    66  
    67  	if err != nil {
    68  		return Buildpack{}, Warnings(warnings), err
    69  	}
    70  
    71  	if len(ccv3Buildpacks) == 0 {
    72  		return Buildpack{}, Warnings(warnings), actionerror.BuildpackNotFoundError{BuildpackName: buildpackName, StackName: buildpackStack}
    73  	}
    74  
    75  	if len(ccv3Buildpacks) > 1 {
    76  		for _, buildpack := range ccv3Buildpacks {
    77  			if buildpack.Stack == "" {
    78  				return Buildpack(buildpack), Warnings(warnings), nil
    79  			}
    80  		}
    81  		return Buildpack{}, Warnings(warnings), actionerror.MultipleBuildpacksFoundError{BuildpackName: buildpackName}
    82  	}
    83  
    84  	return Buildpack(ccv3Buildpacks[0]), Warnings(warnings), err
    85  }
    86  
    87  func (actor Actor) CreateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) {
    88  	ccv3Buildpack, warnings, err := actor.CloudControllerClient.CreateBuildpack(
    89  		ccv3.Buildpack(buildpack),
    90  	)
    91  
    92  	return Buildpack(ccv3Buildpack), Warnings(warnings), err
    93  }
    94  
    95  func (actor Actor) UpdateBuildpackByNameAndStack(buildpackName string, buildpackStack string, buildpack Buildpack) (Buildpack, Warnings, error) {
    96  	var warnings Warnings
    97  	foundBuildpack, getWarnings, err := actor.GetBuildpackByNameAndStack(buildpackName, buildpackStack)
    98  	warnings = append(warnings, getWarnings...)
    99  
   100  	if err != nil {
   101  		return Buildpack{}, warnings, err
   102  	}
   103  
   104  	buildpack.GUID = foundBuildpack.GUID
   105  
   106  	updatedBuildpack, updateWarnings, err := actor.CloudControllerClient.UpdateBuildpack(ccv3.Buildpack(buildpack))
   107  	warnings = append(warnings, updateWarnings...)
   108  	if err != nil {
   109  		return Buildpack{}, warnings, err
   110  	}
   111  
   112  	return Buildpack(updatedBuildpack), warnings, nil
   113  }
   114  
   115  func (actor Actor) UploadBuildpack(guid string, pathToBuildpackBits string, progressBar SimpleProgressBar) (ccv3.JobURL, Warnings, error) {
   116  	wrappedReader, size, err := progressBar.Initialize(pathToBuildpackBits)
   117  	if err != nil {
   118  		return "", Warnings{}, err
   119  	}
   120  
   121  	jobURL, warnings, err := actor.CloudControllerClient.UploadBuildpack(guid, pathToBuildpackBits, wrappedReader, size)
   122  	if err != nil {
   123  		// TODO: Do we actually want to convert this error? Is this the right place?
   124  		if e, ok := err.(ccerror.BuildpackAlreadyExistsForStackError); ok {
   125  			return "", Warnings(warnings), actionerror.BuildpackAlreadyExistsForStackError{Message: e.Message}
   126  		}
   127  		return "", Warnings(warnings), err
   128  	}
   129  
   130  	// TODO: Should we defer the terminate instead?
   131  	progressBar.Terminate()
   132  
   133  	return jobURL, Warnings(warnings), nil
   134  }
   135  
   136  func (actor *Actor) PrepareBuildpackBits(inputPath string, tmpDirPath string, downloader Downloader) (string, error) {
   137  	if util.IsHTTPScheme(inputPath) {
   138  		pathToDownloadedBits, err := downloader.Download(inputPath, tmpDirPath)
   139  		if err != nil {
   140  			return "", err
   141  		}
   142  		return pathToDownloadedBits, nil
   143  	}
   144  
   145  	if filepath.Ext(inputPath) == ".zip" {
   146  		return inputPath, nil
   147  	}
   148  
   149  	info, err := os.Stat(inputPath)
   150  	if err != nil {
   151  		return "", err
   152  	}
   153  
   154  	if info.IsDir() {
   155  		var empty bool
   156  		empty, err = isEmptyDirectory(inputPath)
   157  		if err != nil {
   158  			return "", err
   159  		}
   160  		if empty {
   161  			return "", actionerror.EmptyBuildpackDirectoryError{Path: inputPath}
   162  		}
   163  		archive := filepath.Join(tmpDirPath, filepath.Base(inputPath)) + ".zip"
   164  
   165  		err = Zipit(inputPath, archive, "")
   166  		if err != nil {
   167  			return "", err
   168  		}
   169  		return archive, nil
   170  	}
   171  
   172  	return inputPath, nil
   173  }
   174  
   175  func isEmptyDirectory(name string) (bool, error) {
   176  	f, err := os.Open(name)
   177  	if err != nil {
   178  		return false, err
   179  	}
   180  	defer f.Close()
   181  
   182  	_, err = f.Readdirnames(1)
   183  	if err == io.EOF {
   184  		return true, nil
   185  	}
   186  	return false, err
   187  }
   188  
   189  // Zipit zips the source into a .zip file in the target dir
   190  func Zipit(source, target, prefix string) error {
   191  	// Thanks to Svett Ralchev
   192  	// http://blog.ralch.com/tutorial/golang-working-with-zip/
   193  
   194  	zipfile, err := os.Create(target)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	defer zipfile.Close()
   199  
   200  	if prefix != "" {
   201  		_, err = io.WriteString(zipfile, prefix)
   202  		if err != nil {
   203  			return err
   204  		}
   205  	}
   206  
   207  	archive := zip.NewWriter(zipfile)
   208  	defer archive.Close()
   209  
   210  	err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
   211  		if err != nil {
   212  			return err
   213  		}
   214  
   215  		if path == source {
   216  			return nil
   217  		}
   218  
   219  		header, err := zip.FileInfoHeader(info)
   220  		if err != nil {
   221  			return err
   222  		}
   223  		header.Name, err = filepath.Rel(source, path)
   224  		if err != nil {
   225  			return err
   226  		}
   227  
   228  		header.Name = filepath.ToSlash(header.Name)
   229  		if info.IsDir() {
   230  			header.Name += "/"
   231  			header.SetMode(info.Mode())
   232  		} else {
   233  			header.Method = zip.Deflate
   234  			header.SetMode(fixMode(info.Mode()))
   235  		}
   236  
   237  		writer, err := archive.CreateHeader(header)
   238  		if err != nil {
   239  			return err
   240  		}
   241  
   242  		if info.IsDir() {
   243  			return nil
   244  		}
   245  
   246  		file, err := os.Open(path)
   247  		if err != nil {
   248  			return err
   249  		}
   250  		defer file.Close()
   251  
   252  		_, err = io.Copy(writer, file)
   253  		return err
   254  	})
   255  
   256  	return err
   257  }
   258  
   259  func (actor Actor) DeleteBuildpackByNameAndStack(buildpackName string, buildpackStack string) (Warnings, error) {
   260  	var allWarnings Warnings
   261  	buildpack, getBuildpackWarnings, err := actor.GetBuildpackByNameAndStack(buildpackName, buildpackStack)
   262  	allWarnings = append(allWarnings, getBuildpackWarnings...)
   263  	if err != nil {
   264  		return allWarnings, err
   265  	}
   266  
   267  	jobURL, deleteBuildpackWarnings, err := actor.CloudControllerClient.DeleteBuildpack(buildpack.GUID)
   268  	allWarnings = append(allWarnings, deleteBuildpackWarnings...)
   269  	if err != nil {
   270  		return allWarnings, err
   271  	}
   272  
   273  	pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL)
   274  	allWarnings = append(allWarnings, pollWarnings...)
   275  
   276  	return allWarnings, err
   277  }