github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/integration/helpers/app.go (about)

     1  package helpers
     2  
     3  import (
     4  	"archive/zip"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/onsi/gomega/gbytes"
    14  
    15  	. "github.com/onsi/gomega"
    16  	. "github.com/onsi/gomega/gexec"
    17  	"gopkg.in/yaml.v2"
    18  )
    19  
    20  // CreateApp creates an empty app in CloudController with no package or droplet
    21  func CreateApp(app string) {
    22  	Eventually(CF("create-app", app)).Should(Exit(0))
    23  }
    24  
    25  // QuickDeleteApp deletes the app with the given name, if provided, using
    26  // 'cf curl /v3/app... -X DELETE'.
    27  func QuickDeleteApp(appName string) {
    28  	guid := AppGUID(appName)
    29  	url := fmt.Sprintf("/v3/apps/%s", guid)
    30  	session := CF("curl", "-X", "DELETE", url)
    31  	Eventually(session).Should(Exit(0))
    32  }
    33  
    34  // WithHelloWorldApp creates a simple application to use with your CLI command
    35  // (typically CF Push). When pushing, be aware of specifying '-b
    36  // staticfile_buildpack" so that your app will correctly start up with the
    37  // proper buildpack.
    38  func WithHelloWorldApp(f func(dir string)) {
    39  	dir := TempDirAbsolutePath("", "simple-app")
    40  	defer os.RemoveAll(dir)
    41  
    42  	tempfile := filepath.Join(dir, "index.html")
    43  	err := ioutil.WriteFile(tempfile, []byte(fmt.Sprintf("hello world %d", rand.Int())), 0666)
    44  	Expect(err).ToNot(HaveOccurred())
    45  
    46  	err = ioutil.WriteFile(filepath.Join(dir, "Staticfile"), nil, 0666)
    47  	Expect(err).ToNot(HaveOccurred())
    48  
    49  	f(dir)
    50  }
    51  
    52  // WithMultiEndpointApp creates a simple application to use with your CLI command
    53  // (typically CF Push). It has multiple endpoints which are helpful when testing
    54  // http healthchecks.
    55  func WithMultiEndpointApp(f func(dir string)) {
    56  	dir := TempDirAbsolutePath("", "simple-app")
    57  	defer os.RemoveAll(dir)
    58  
    59  	tempfile := filepath.Join(dir, "index.html")
    60  	err := ioutil.WriteFile(tempfile, []byte(fmt.Sprintf("hello world %d", rand.Int())), 0666)
    61  	Expect(err).ToNot(HaveOccurred())
    62  
    63  	tempfile = filepath.Join(dir, "other_endpoint.html")
    64  	err = ioutil.WriteFile(tempfile, []byte("other endpoint"), 0666)
    65  	Expect(err).ToNot(HaveOccurred())
    66  
    67  	tempfile = filepath.Join(dir, "third_endpoint.html")
    68  	err = ioutil.WriteFile(tempfile, []byte("third endpoint"), 0666)
    69  	Expect(err).ToNot(HaveOccurred())
    70  
    71  	err = ioutil.WriteFile(filepath.Join(dir, "Staticfile"), nil, 0666)
    72  	Expect(err).ToNot(HaveOccurred())
    73  
    74  	f(dir)
    75  }
    76  
    77  func WithSidecarApp(f func(dir string), appName string) {
    78  	withSidecarManifest := func(dir string) {
    79  		err := ioutil.WriteFile(filepath.Join(dir, "manifest.yml"), []byte(fmt.Sprintf(`---
    80  applications:
    81    - name: %s
    82      sidecars:
    83      - name: sidecar_name
    84        process_types: [web]
    85        command: sleep infinity`,
    86  			appName)), 0666)
    87  		Expect(err).ToNot(HaveOccurred())
    88  
    89  		f(dir)
    90  	}
    91  
    92  	WithHelloWorldApp(withSidecarManifest)
    93  }
    94  
    95  func WithTaskApp(f func(dir string), appName string) {
    96  	withTaskManifest := func(dir string) {
    97  		err := ioutil.WriteFile(filepath.Join(dir, "manifest.yml"), []byte(fmt.Sprintf(`---
    98  applications:
    99  - name: %s
   100    processes:
   101    - type: task
   102      command: echo hi`,
   103  			appName)), 0666)
   104  		Expect(err).ToNot(HaveOccurred())
   105  
   106  		f(dir)
   107  	}
   108  
   109  	WithHelloWorldApp(withTaskManifest)
   110  }
   111  
   112  // WithNoResourceMatchedApp creates a simple application to use with your CLI
   113  // command (typically CF Push). When pushing, be aware of specifying '-b
   114  // staticfile_buildpack" so that your app will correctly start up with the
   115  // proper buildpack.
   116  func WithNoResourceMatchedApp(f func(dir string)) {
   117  	dir := TempDirAbsolutePath("", "simple-app")
   118  	defer os.RemoveAll(dir)
   119  
   120  	tempfile := filepath.Join(dir, "index.html")
   121  
   122  	err := ioutil.WriteFile(tempfile, []byte(fmt.Sprintf("hello world %s", strings.Repeat("a", 65*1024))), 0666)
   123  	Expect(err).ToNot(HaveOccurred())
   124  
   125  	f(dir)
   126  }
   127  
   128  // WithMultiBuildpackApp creates a multi-buildpack application to use with the CF push command.
   129  func WithMultiBuildpackApp(f func(dir string)) {
   130  	f("../../assets/go_calls_ruby")
   131  }
   132  
   133  // WithProcfileApp creates an application to use with your CLI command
   134  // that contains Procfile defining web and worker processes.
   135  func WithProcfileApp(f func(dir string)) {
   136  	dir := TempDirAbsolutePath("", "simple-ruby-app")
   137  	defer os.RemoveAll(dir)
   138  
   139  	err := ioutil.WriteFile(filepath.Join(dir, "Procfile"), []byte(`---
   140  web: ruby -run -e httpd . -p $PORT
   141  console: bundle exec irb`,
   142  	), 0666)
   143  	Expect(err).ToNot(HaveOccurred())
   144  
   145  	//TODO: Remove the ruby version once bundler issue(https://github.com/rubygems/rubygems/issues/6280)
   146  	// is solved
   147  	err = ioutil.WriteFile(filepath.Join(dir, "Gemfile"), []byte(`source 'http://rubygems.org'
   148  ruby '~> 3.0'
   149  gem 'irb'
   150  gem 'webrick'`,
   151  	), 0666)
   152  	Expect(err).ToNot(HaveOccurred())
   153  
   154  	err = ioutil.WriteFile(filepath.Join(dir, "Gemfile.lock"), []byte(`
   155  GEM
   156    remote: http://rubygems.org/
   157    specs:
   158      io-console (0.6.0)
   159      irb (1.6.4)
   160        reline (>= 0.3.0)
   161      reline (0.3.3)
   162        io-console (~> 0.5)
   163      webrick (1.8.1)
   164  
   165  PLATFORMS
   166    ruby
   167  
   168  DEPENDENCIES
   169    irb
   170    webrick
   171  `,
   172  	), 0666)
   173  	Expect(err).ToNot(HaveOccurred())
   174  
   175  	f(dir)
   176  }
   177  
   178  // WithCrashingApp creates an application to use with your CLI command
   179  // that will not successfully start its `web` process
   180  func WithCrashingApp(f func(dir string)) {
   181  	dir := TempDirAbsolutePath("", "crashing-ruby-app")
   182  	defer os.RemoveAll(dir)
   183  
   184  	err := ioutil.WriteFile(filepath.Join(dir, "Procfile"), []byte(`---
   185  web: bogus bogus`,
   186  	), 0666)
   187  	Expect(err).ToNot(HaveOccurred())
   188  
   189  	err = ioutil.WriteFile(filepath.Join(dir, "Gemfile"), nil, 0666)
   190  	Expect(err).ToNot(HaveOccurred())
   191  
   192  	err = ioutil.WriteFile(filepath.Join(dir, "Gemfile.lock"), []byte(`
   193  GEM
   194    specs:
   195  
   196  PLATFORMS
   197    ruby
   198  
   199  DEPENDENCIES
   200  
   201  BUNDLED WITH
   202     2.1.4
   203  	`), 0666)
   204  	Expect(err).ToNot(HaveOccurred())
   205  
   206  	f(dir)
   207  }
   208  
   209  // WithBananaPantsApp creates a simple application to use with your CLI command
   210  // (typically CF Push). When pushing, be aware of specifying '-b
   211  // staticfile_buildpack" so that your app will correctly start up with the
   212  // proper buildpack.
   213  func WithBananaPantsApp(f func(dir string)) {
   214  	dir := TempDirAbsolutePath("", "simple-app")
   215  	defer os.RemoveAll(dir)
   216  
   217  	tempfile := filepath.Join(dir, "index.html")
   218  	err := ioutil.WriteFile(tempfile, []byte("Banana Pants"), 0666)
   219  	Expect(err).ToNot(HaveOccurred())
   220  
   221  	err = ioutil.WriteFile(filepath.Join(dir, "Staticfile"), nil, 0666)
   222  	Expect(err).ToNot(HaveOccurred())
   223  
   224  	f(dir)
   225  }
   226  
   227  func WithEmptyFilesApp(f func(dir string)) {
   228  	dir := TempDirAbsolutePath("", "simple-app")
   229  	defer os.RemoveAll(dir)
   230  
   231  	tempfile := filepath.Join(dir, "index.html")
   232  	err := ioutil.WriteFile(tempfile, nil, 0666)
   233  	Expect(err).ToNot(HaveOccurred())
   234  
   235  	err = ioutil.WriteFile(filepath.Join(dir, "Staticfile"), nil, 0666)
   236  	Expect(err).ToNot(HaveOccurred())
   237  
   238  	f(dir)
   239  }
   240  
   241  // AppGUID returns the GUID for an app in the currently targeted space.
   242  func AppGUID(appName string) string {
   243  	session := CF("app", appName, "--guid")
   244  	Eventually(session).Should(Exit(0))
   245  	return strings.TrimSpace(string(session.Out.Contents()))
   246  }
   247  
   248  // AppJSON returns the JSON representation of an app by name.
   249  func AppJSON(appName string) string {
   250  	appGUID := AppGUID(appName)
   251  	session := CF("curl", fmt.Sprintf("/v3/apps/%s", appGUID))
   252  	Eventually(session).Should(Exit(0))
   253  	return strings.TrimSpace(string(session.Out.Contents()))
   254  }
   255  
   256  // WriteManifest will write out a YAML manifest file at the specified path.
   257  func WriteManifest(path string, manifest map[string]interface{}) {
   258  	body, err := yaml.Marshal(manifest)
   259  	Expect(err).ToNot(HaveOccurred())
   260  	err = ioutil.WriteFile(path, body, 0666)
   261  	Expect(err).ToNot(HaveOccurred())
   262  }
   263  
   264  func ReadManifest(path string) map[string]interface{} {
   265  	manifestBytes, err := ioutil.ReadFile(path)
   266  	Expect(err).ToNot(HaveOccurred())
   267  	var manifest map[string]interface{}
   268  	err = yaml.Unmarshal(manifestBytes, &manifest)
   269  	Expect(err).ToNot(HaveOccurred())
   270  	return manifest
   271  }
   272  
   273  // Zipit zips the source into a .zip file in the target dir.
   274  func Zipit(source, target, prefix string) error {
   275  	// Thanks to Svett Ralchev
   276  	// http://blog.ralch.com/tutorial/golang-working-with-zip/
   277  
   278  	zipfile, err := os.Create(target)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	defer zipfile.Close()
   283  
   284  	if prefix != "" {
   285  		_, err = io.WriteString(zipfile, prefix)
   286  		if err != nil {
   287  			return err
   288  		}
   289  	}
   290  
   291  	archive := zip.NewWriter(zipfile)
   292  	defer archive.Close()
   293  
   294  	err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
   295  		if err != nil {
   296  			return err
   297  		}
   298  
   299  		if path == source {
   300  			return nil
   301  		}
   302  
   303  		header, err := zip.FileInfoHeader(info)
   304  		if err != nil {
   305  			return err
   306  		}
   307  		header.Name, err = filepath.Rel(source, path)
   308  		if err != nil {
   309  			return err
   310  		}
   311  
   312  		header.Name = filepath.ToSlash(header.Name)
   313  
   314  		if info.IsDir() {
   315  			header.Name += "/"
   316  			header.SetMode(0755)
   317  		} else {
   318  			header.Method = zip.Deflate
   319  			header.SetMode(0744)
   320  		}
   321  
   322  		writer, err := archive.CreateHeader(header)
   323  		if err != nil {
   324  			return err
   325  		}
   326  
   327  		if info.IsDir() {
   328  			return nil
   329  		}
   330  
   331  		file, err := os.Open(path)
   332  		if err != nil {
   333  			return err
   334  		}
   335  		defer file.Close()
   336  
   337  		_, err = io.Copy(writer, file)
   338  		return err
   339  	})
   340  
   341  	return err
   342  }
   343  
   344  // ConfirmStagingLogs checks session for log output
   345  // indicating that staging is working.
   346  func ConfirmStagingLogs(session *Session) {
   347  	Eventually(session).Should(gbytes.Say(`(?i)Creating container|Successfully created container|Staging\.\.\.|Staging process started \.\.\.|Staging Complete|Exit status 0|Uploading droplet\.\.\.|Uploading complete`))
   348  }
   349  
   350  func WaitForAppMemoryToTakeEffect(appName string, processIndex int, instanceIndex int, shouldRestartFirst bool, expectedMemory string) {
   351  	if shouldRestartFirst {
   352  		session := CF("restart", appName)
   353  		Eventually(session).Should(Exit(0))
   354  	}
   355  
   356  	Eventually(func() string {
   357  		session := CF("app", appName)
   358  		Eventually(session).Should(Exit(0))
   359  		appTable := ParseV3AppProcessTable(session.Out.Contents())
   360  		return appTable.Processes[processIndex].Instances[instanceIndex].Memory
   361  	}).Should(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]? of %s`, expectedMemory)))
   362  }
   363  
   364  func WaitForAppDiskToTakeEffect(appName string, processIndex int, instanceIndex int, shouldRestartFirst bool, expectedDisk string) {
   365  	if shouldRestartFirst {
   366  		session := CF("restart", appName)
   367  		Eventually(session).Should(Exit(0))
   368  	}
   369  
   370  	Eventually(func() string {
   371  		session := CF("app", appName)
   372  		Eventually(session).Should(Exit(0))
   373  		appTable := ParseV3AppProcessTable(session.Out.Contents())
   374  		return appTable.Processes[processIndex].Instances[instanceIndex].Disk
   375  	}).Should(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]? of %s`, expectedDisk)))
   376  }
   377  
   378  func WaitForLogRateLimitToTakeEffect(appName string, processIndex int, instanceIndex int, shouldRestartFirst bool, expectedLogRateLimit string) {
   379  	if shouldRestartFirst {
   380  		session := CF("restart", appName)
   381  		Eventually(session).Should(Exit(0))
   382  	}
   383  
   384  	Eventually(func() string {
   385  		session := CF("app", appName)
   386  		Eventually(session).Should(Exit(0))
   387  		appTable := ParseV3AppProcessTable(session.Out.Contents())
   388  		return appTable.Processes[processIndex].Instances[instanceIndex].LogRate
   389  	}).Should(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]?/s of %s/s`, expectedLogRateLimit)))
   390  }