github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/acceptance/config/asset_manager.go (about)

     1  //go:build acceptance
     2  // +build acceptance
     3  
     4  package config
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"regexp"
    12  	"testing"
    13  
    14  	acceptanceOS "github.com/buildpacks/pack/acceptance/os"
    15  	"github.com/buildpacks/pack/internal/builder"
    16  	"github.com/buildpacks/pack/internal/config"
    17  	"github.com/buildpacks/pack/internal/style"
    18  	"github.com/buildpacks/pack/pkg/blob"
    19  	h "github.com/buildpacks/pack/testhelpers"
    20  )
    21  
    22  const (
    23  	defaultCompilePackVersion = "0.0.0"
    24  )
    25  
    26  var (
    27  	currentPackFixturesDir           = filepath.Join("testdata", "pack_fixtures")
    28  	previousPackFixturesOverridesDir = filepath.Join("testdata", "pack_previous_fixtures_overrides")
    29  	lifecycleTgzExp                  = regexp.MustCompile(`lifecycle-v\d+.\d+.\d+(-pre.\d+)?(-rc.\d+)?\+linux.x86-64.tgz`)
    30  )
    31  
    32  type AssetManager struct {
    33  	packPath                    string
    34  	packFixturesPath            string
    35  	previousPackPath            string
    36  	previousPackFixturesPaths   []string
    37  	githubAssetFetcher          *GithubAssetFetcher
    38  	lifecyclePath               string
    39  	lifecycleDescriptor         builder.LifecycleDescriptor
    40  	lifecycleImage              string
    41  	previousLifecyclePath       string
    42  	previousLifecycleDescriptor builder.LifecycleDescriptor
    43  	previousLifecycleImage      string
    44  	defaultLifecycleDescriptor  builder.LifecycleDescriptor
    45  	testObject                  *testing.T
    46  }
    47  
    48  func ConvergedAssetManager(t *testing.T, assert h.AssertionManager, inputConfig InputConfigurationManager) AssetManager {
    49  	t.Helper()
    50  
    51  	var (
    52  		convergedCurrentPackPath             string
    53  		convergedPreviousPackPath            string
    54  		convergedPreviousPackFixturesPaths   []string
    55  		convergedCurrentLifecyclePath        string
    56  		convergedCurrentLifecycleImage       string
    57  		convergedCurrentLifecycleDescriptor  builder.LifecycleDescriptor
    58  		convergedPreviousLifecyclePath       string
    59  		convergedPreviousLifecycleImage      string
    60  		convergedPreviousLifecycleDescriptor builder.LifecycleDescriptor
    61  		convergedDefaultLifecycleDescriptor  builder.LifecycleDescriptor
    62  	)
    63  
    64  	githubAssetFetcher, err := NewGithubAssetFetcher(t, inputConfig.githubToken)
    65  	h.AssertNil(t, err)
    66  
    67  	assetBuilder := assetManagerBuilder{
    68  		testObject:         t,
    69  		assert:             assert,
    70  		inputConfig:        inputConfig,
    71  		githubAssetFetcher: githubAssetFetcher,
    72  	}
    73  
    74  	if inputConfig.combinations.requiresCurrentPack() {
    75  		convergedCurrentPackPath = assetBuilder.ensureCurrentPack()
    76  	}
    77  
    78  	if inputConfig.combinations.requiresPreviousPack() {
    79  		convergedPreviousPackPath = assetBuilder.ensurePreviousPack()
    80  		convergedPreviousPackFixturesPath := assetBuilder.ensurePreviousPackFixtures()
    81  
    82  		convergedPreviousPackFixturesPaths = []string{previousPackFixturesOverridesDir, convergedPreviousPackFixturesPath}
    83  	}
    84  
    85  	if inputConfig.combinations.requiresCurrentLifecycle() {
    86  		convergedCurrentLifecyclePath, convergedCurrentLifecycleImage, convergedCurrentLifecycleDescriptor = assetBuilder.ensureCurrentLifecycle()
    87  	}
    88  
    89  	if inputConfig.combinations.requiresPreviousLifecycle() {
    90  		convergedPreviousLifecyclePath, convergedPreviousLifecycleImage, convergedPreviousLifecycleDescriptor = assetBuilder.ensurePreviousLifecycle()
    91  	}
    92  
    93  	if inputConfig.combinations.requiresDefaultLifecycle() {
    94  		convergedDefaultLifecycleDescriptor = assetBuilder.defaultLifecycleDescriptor()
    95  	}
    96  
    97  	return AssetManager{
    98  		packPath:                    convergedCurrentPackPath,
    99  		packFixturesPath:            currentPackFixturesDir,
   100  		previousPackPath:            convergedPreviousPackPath,
   101  		previousPackFixturesPaths:   convergedPreviousPackFixturesPaths,
   102  		lifecyclePath:               convergedCurrentLifecyclePath,
   103  		lifecycleImage:              convergedCurrentLifecycleImage,
   104  		lifecycleDescriptor:         convergedCurrentLifecycleDescriptor,
   105  		previousLifecyclePath:       convergedPreviousLifecyclePath,
   106  		previousLifecycleImage:      convergedPreviousLifecycleImage,
   107  		previousLifecycleDescriptor: convergedPreviousLifecycleDescriptor,
   108  		defaultLifecycleDescriptor:  convergedDefaultLifecycleDescriptor,
   109  		testObject:                  t,
   110  	}
   111  }
   112  
   113  func (a AssetManager) PackPaths(kind ComboValue) (packPath string, packFixturesPaths []string) {
   114  	a.testObject.Helper()
   115  
   116  	switch kind {
   117  	case Current:
   118  		packPath = a.packPath
   119  		packFixturesPaths = []string{a.packFixturesPath}
   120  	case Previous:
   121  		packPath = a.previousPackPath
   122  		packFixturesPaths = a.previousPackFixturesPaths
   123  	default:
   124  		a.testObject.Fatalf("pack kind must be current or previous, was %s", kind)
   125  	}
   126  
   127  	return packPath, packFixturesPaths
   128  }
   129  
   130  func (a AssetManager) LifecyclePath(kind ComboValue) string {
   131  	a.testObject.Helper()
   132  
   133  	switch kind {
   134  	case Current:
   135  		return a.lifecyclePath
   136  	case Previous:
   137  		return a.previousLifecyclePath
   138  	case DefaultKind:
   139  		return ""
   140  	}
   141  
   142  	a.testObject.Fatalf("lifecycle kind must be previous, current or default was %s", kind)
   143  	return "" // Unreachable
   144  }
   145  
   146  func (a AssetManager) LifecycleDescriptor(kind ComboValue) builder.LifecycleDescriptor {
   147  	a.testObject.Helper()
   148  
   149  	switch kind {
   150  	case Current:
   151  		return a.lifecycleDescriptor
   152  	case Previous:
   153  		return a.previousLifecycleDescriptor
   154  	case DefaultKind:
   155  		return a.defaultLifecycleDescriptor
   156  	}
   157  
   158  	a.testObject.Fatalf("lifecycle kind must be previous, current or default was %s", kind)
   159  	return builder.LifecycleDescriptor{} // Unreachable
   160  }
   161  
   162  func (a AssetManager) LifecycleImage(kind ComboValue) string {
   163  	a.testObject.Helper()
   164  
   165  	switch kind {
   166  	case Current:
   167  		return a.lifecycleImage
   168  	case Previous:
   169  		return a.previousLifecycleImage
   170  	case DefaultKind:
   171  		return fmt.Sprintf("%s:%s", config.DefaultLifecycleImageRepo, a.defaultLifecycleDescriptor.Info.Version)
   172  	}
   173  
   174  	a.testObject.Fatalf("lifecycle kind must be previous, current or default was %s", kind)
   175  	return "" // Unreachable
   176  }
   177  
   178  type assetManagerBuilder struct {
   179  	testObject         *testing.T
   180  	assert             h.AssertionManager
   181  	inputConfig        InputConfigurationManager
   182  	githubAssetFetcher *GithubAssetFetcher
   183  }
   184  
   185  func (b assetManagerBuilder) ensureCurrentPack() string {
   186  	b.testObject.Helper()
   187  
   188  	if b.inputConfig.packPath != "" {
   189  		return b.inputConfig.packPath
   190  	}
   191  
   192  	compileWithVersion := b.inputConfig.compilePackWithVersion
   193  	if compileWithVersion == "" {
   194  		compileWithVersion = defaultCompilePackVersion
   195  	}
   196  
   197  	return b.buildPack(compileWithVersion)
   198  }
   199  
   200  func (b assetManagerBuilder) ensurePreviousPack() string {
   201  	b.testObject.Helper()
   202  
   203  	if b.inputConfig.previousPackPath != "" {
   204  		return b.inputConfig.previousPackPath
   205  	}
   206  
   207  	b.testObject.Logf(
   208  		"run combinations %+v require %s to be set",
   209  		b.inputConfig.combinations,
   210  		style.Symbol(envPreviousPackPath),
   211  	)
   212  
   213  	version, err := b.githubAssetFetcher.FetchReleaseVersion("buildpacks", "pack", 0)
   214  	b.assert.Nil(err)
   215  
   216  	assetDir, err := b.githubAssetFetcher.FetchReleaseAsset(
   217  		"buildpacks",
   218  		"pack",
   219  		version,
   220  		acceptanceOS.PackBinaryExp,
   221  		true,
   222  	)
   223  	b.assert.Nil(err)
   224  	assetPath := filepath.Join(assetDir, acceptanceOS.PackBinaryName)
   225  
   226  	b.testObject.Logf("using %s for previous pack path", assetPath)
   227  
   228  	return assetPath
   229  }
   230  
   231  func (b assetManagerBuilder) ensurePreviousPackFixtures() string {
   232  	b.testObject.Helper()
   233  
   234  	if b.inputConfig.previousPackFixturesPath != "" {
   235  		return b.inputConfig.previousPackFixturesPath
   236  	}
   237  
   238  	b.testObject.Logf(
   239  		"run combinations %+v require %s to be set",
   240  		b.inputConfig.combinations,
   241  		style.Symbol(envPreviousPackFixturesPath),
   242  	)
   243  
   244  	version, err := b.githubAssetFetcher.FetchReleaseVersion("buildpacks", "pack", 0)
   245  	b.assert.Nil(err)
   246  
   247  	sourceDir, err := b.githubAssetFetcher.FetchReleaseSource("buildpacks", "pack", version)
   248  	b.assert.Nil(err)
   249  
   250  	sourceDirFiles, err := os.ReadDir(sourceDir)
   251  	b.assert.Nil(err)
   252  	// GitHub source tarballs have a top-level directory whose name includes the current commit sha.
   253  	innerDir := sourceDirFiles[0].Name()
   254  	fixturesDir := filepath.Join(sourceDir, innerDir, "acceptance", "testdata", "pack_fixtures")
   255  
   256  	b.testObject.Logf("using %s for previous pack fixtures path", fixturesDir)
   257  
   258  	return fixturesDir
   259  }
   260  
   261  func (b assetManagerBuilder) ensureCurrentLifecycle() (string, string, builder.LifecycleDescriptor) {
   262  	b.testObject.Helper()
   263  
   264  	lifecyclePath := b.inputConfig.lifecyclePath
   265  
   266  	if lifecyclePath == "" {
   267  		b.testObject.Logf(
   268  			"run combinations %+v require %s to be set",
   269  			b.inputConfig.combinations,
   270  			style.Symbol(envLifecyclePath),
   271  		)
   272  
   273  		lifecyclePath = b.downloadLifecycleRelative(0)
   274  
   275  		b.testObject.Logf("using %s for current lifecycle path", lifecyclePath)
   276  	}
   277  
   278  	lifecycle, err := builder.NewLifecycle(blob.NewBlob(lifecyclePath))
   279  	b.assert.Nil(err)
   280  
   281  	lifecycleImage := b.inputConfig.lifecycleImage
   282  
   283  	if lifecycleImage == "" {
   284  		lifecycleImage = fmt.Sprintf("%s:%s", config.DefaultLifecycleImageRepo, lifecycle.Descriptor().Info.Version)
   285  
   286  		b.testObject.Logf("using %s for current lifecycle image", lifecycleImage)
   287  	}
   288  
   289  	return lifecyclePath, lifecycleImage, lifecycle.Descriptor()
   290  }
   291  
   292  func (b assetManagerBuilder) ensurePreviousLifecycle() (string, string, builder.LifecycleDescriptor) {
   293  	b.testObject.Helper()
   294  
   295  	previousLifecyclePath := b.inputConfig.previousLifecyclePath
   296  
   297  	if previousLifecyclePath == "" {
   298  		b.testObject.Logf(
   299  			"run combinations %+v require %s to be set",
   300  			b.inputConfig.combinations,
   301  			style.Symbol(envPreviousLifecyclePath),
   302  		)
   303  
   304  		previousLifecyclePath = b.downloadLifecycleRelative(-1)
   305  
   306  		b.testObject.Logf("using %s for previous lifecycle path", previousLifecyclePath)
   307  	}
   308  
   309  	lifecycle, err := builder.NewLifecycle(blob.NewBlob(previousLifecyclePath))
   310  	b.assert.Nil(err)
   311  
   312  	previousLifecycleImage := b.inputConfig.previousLifecycleImage
   313  
   314  	if previousLifecycleImage == "" {
   315  		previousLifecycleImage = fmt.Sprintf("%s:%s", config.DefaultLifecycleImageRepo, lifecycle.Descriptor().Info.Version)
   316  
   317  		b.testObject.Logf("using %s for previous lifecycle image", previousLifecycleImage)
   318  	}
   319  
   320  	return previousLifecyclePath, previousLifecycleImage, lifecycle.Descriptor()
   321  }
   322  
   323  func (b assetManagerBuilder) downloadLifecycle(version string) string {
   324  	path, err := b.githubAssetFetcher.FetchReleaseAsset(
   325  		"buildpacks",
   326  		"lifecycle",
   327  		version,
   328  		lifecycleTgzExp,
   329  		false,
   330  	)
   331  	b.assert.Nil(err)
   332  
   333  	return path
   334  }
   335  
   336  func (b assetManagerBuilder) downloadLifecycleRelative(relativeVersion int) string {
   337  	b.testObject.Helper()
   338  
   339  	version, err := b.githubAssetFetcher.FetchReleaseVersion("buildpacks", "lifecycle", relativeVersion)
   340  	b.assert.Nil(err)
   341  
   342  	return b.downloadLifecycle(version)
   343  }
   344  
   345  func (b assetManagerBuilder) buildPack(compileVersion string) string {
   346  	b.testObject.Helper()
   347  
   348  	packTmpDir, err := os.MkdirTemp("", "pack.acceptance.binary.")
   349  	b.assert.Nil(err)
   350  
   351  	packPath := filepath.Join(packTmpDir, acceptanceOS.PackBinaryName)
   352  
   353  	cwd, err := os.Getwd()
   354  	b.assert.Nil(err)
   355  
   356  	cmd := exec.Command("go", "build",
   357  		"-ldflags", fmt.Sprintf("-X 'github.com/buildpacks/pack/cmd.Version=%s'", compileVersion),
   358  		"-o", packPath,
   359  		"./cmd/pack",
   360  	)
   361  	if filepath.Base(cwd) == "acceptance" {
   362  		cmd.Dir = filepath.Dir(cwd)
   363  	}
   364  
   365  	b.testObject.Logf("building pack: [CWD=%s] %s", cmd.Dir, cmd.Args)
   366  	_, err = cmd.CombinedOutput()
   367  	b.assert.Nil(err)
   368  
   369  	return packPath
   370  }
   371  
   372  func (b assetManagerBuilder) defaultLifecycleDescriptor() builder.LifecycleDescriptor {
   373  	lifecyclePath := b.downloadLifecycle("v" + builder.DefaultLifecycleVersion)
   374  
   375  	b.testObject.Logf("using %s for default lifecycle path", lifecyclePath)
   376  
   377  	lifecycle, err := builder.NewLifecycle(blob.NewBlob(lifecyclePath))
   378  	b.assert.Nil(err)
   379  
   380  	return lifecycle.Descriptor()
   381  }