diff --git a/default_conf.go b/assets/default.conf similarity index 97% rename from default_conf.go rename to assets/default.conf index 56c50540..99f5c438 100644 --- a/default_conf.go +++ b/assets/default.conf @@ -1,6 +1,4 @@ -package nginx - -const defaultConf string = `# Number of worker processes running in container +# Number of worker processes running in container worker_processes 1; # Run NGINX in foreground (necessary for containerized NGINX) @@ -172,7 +170,7 @@ $((- if (ne .BasicAuthFile "") )) auth_basic_user_file $(( .BasicAuthFile )); $(( end )) location $(( .WebServerLocationPath )) { -$((- if .WebServerPushStateEnabled )) +$((- if .WebServerEnablePushState )) # Send the content at / in response to *any* requested endpoint if (!-e $request_filename) { rewrite ^(.*)$ / break; @@ -191,4 +189,3 @@ $(( end )) } } } -` diff --git a/build.go b/build.go index ee5cf390..caf68ed1 100644 --- a/build.go +++ b/build.go @@ -10,11 +10,9 @@ import ( "github.com/Masterminds/semver" "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/chronos" - "github.com/paketo-buildpacks/packit/v2/fs" "github.com/paketo-buildpacks/packit/v2/postal" "github.com/paketo-buildpacks/packit/v2/sbom" "github.com/paketo-buildpacks/packit/v2/scribe" - "github.com/paketo-buildpacks/packit/v2/servicebindings" ) //go:generate faux --interface EntryResolver --output fakes/entry_resolver.go @@ -35,14 +33,9 @@ type Calculator interface { Sum(paths ...string) (string, error) } -//go:generate faux --interface Bindings --output fakes/binding_resolver.go -type Bindings interface { - Resolve(typ, provider, platformDir string) ([]servicebindings.Binding, error) -} - //go:generate faux --interface ConfigGenerator --output fakes/config_generator.go type ConfigGenerator interface { - Generate(env BuildEnvironment) error + Generate(config Configuration) error } //go:generate faux --interface SBOMGenerator --output fakes/sbom_generator.go @@ -50,23 +43,10 @@ type SBOMGenerator interface { GenerateFromDependency(dependency postal.Dependency, dir string) (sbom.SBOM, error) } -type BuildEnvironment struct { - BasicAuthFile string - ConfLocation string `env:"BP_NGINX_CONF_LOCATION"` - NginxVersion string `env:"BP_NGINX_VERSION"` - Reload bool `env:"BP_LIVE_RELOAD_ENABLED"` - WebServer string `env:"BP_WEB_SERVER"` - WebServerForceHTTPS bool `env:"BP_WEB_SERVER_FORCE_HTTPS"` - WebServerPushStateEnabled bool `env:"BP_WEB_SERVER_ENABLE_PUSH_STATE"` - WebServerRoot string `env:"BP_WEB_SERVER_ROOT"` - WebServerLocationPath string `env:"BP_WEB_SERVER_LOCATION_PATH"` -} - -func Build(buildEnv BuildEnvironment, +func Build(config Configuration, entryResolver EntryResolver, dependencyService DependencyService, - bindings Bindings, - config ConfigGenerator, + configGenerator ConfigGenerator, calculator Calculator, sbomGenerator SBOMGenerator, logger scribe.Emitter, @@ -76,17 +56,14 @@ func Build(buildEnv BuildEnvironment, logger.Title("%s %s", context.BuildpackInfo.Name, context.BuildpackInfo.Version) logger.Process("Resolving Nginx Server version") - - priorities := []interface{}{ + entry, sortedEntries := entryResolver.Resolve("nginx", context.Plan.Entries, []interface{}{ "BP_NGINX_VERSION", "buildpack.yml", "buildpack.toml", - } - entry, sortedEntries := entryResolver.Resolve("nginx", context.Plan.Entries, priorities) - entryVersion, _ := entry.Metadata["version"].(string) - + }) logger.Candidates(sortedEntries) + entryVersion, _ := entry.Metadata["version"].(string) dependency, err := dependencyService.Resolve(filepath.Join(context.CNBPath, "buildpack.toml"), entry.Name, entryVersion, context.Stack) if err != nil { return packit.BuildResult{}, err @@ -94,60 +71,46 @@ func Build(buildEnv BuildEnvironment, logger.SelectedDependency(entry, dependency, clock.Now()) - versionSource := entry.Metadata["version-source"] - if versionSource != nil { - if versionSource.(string) == "buildpack.yml" { - nextMajorVersion := semver.MustParse(context.BuildpackInfo.Version).IncMajor() - logger.Subprocess("WARNING: Setting the server version through buildpack.yml will be deprecated soon in Nginx Server Buildpack v%s.", nextMajorVersion.String()) - logger.Subprocess("Please specify the version through the $BP_NGINX_VERSION environment variable instead. See docs for more information.") - logger.Break() - } + versionSource, _ := entry.Metadata["version-source"].(string) + if versionSource == "buildpack.yml" { + nextMajorVersion := semver.MustParse(context.BuildpackInfo.Version).IncMajor() + logger.Subprocess("WARNING: Setting the server version through buildpack.yml will be deprecated soon in Nginx Server Buildpack v%s.", nextMajorVersion.String()) + logger.Subprocess("Please specify the version through the $BP_NGINX_VERSION environment variable instead. See docs for more information.") + logger.Break() } - err = os.MkdirAll(filepath.Join(context.WorkingDir, "logs"), os.ModePerm) - if err != nil { - return packit.BuildResult{}, fmt.Errorf("failed to create logs dir : %w", err) + if !filepath.IsAbs(config.NGINXConfLocation) { + config.NGINXConfLocation = filepath.Join(context.WorkingDir, config.NGINXConfLocation) } - buildEnv.ConfLocation = cleanNginxConfLocation(buildEnv.ConfLocation, context.WorkingDir) - - if buildEnv.WebServer == "nginx" { - bindingSet, err := bindings.Resolve("htpasswd", "", context.Platform.Path) - if err != nil { - return packit.BuildResult{}, err - } - - if len(bindingSet) > 1 { - return packit.BuildResult{}, fmt.Errorf("binding resolver found more than one binding of type 'htpasswd'") - } - - if len(bindingSet) == 1 { - if _, ok := bindingSet[0].Entries[".htpasswd"]; !ok { - return packit.BuildResult{}, fmt.Errorf("binding of type 'htpasswd' does not contain required entry '.htpasswd'") - } - buildEnv.BasicAuthFile = filepath.Join(bindingSet[0].Path, ".htpasswd") - } - - err = config.Generate(buildEnv) + if config.WebServer == "nginx" { + err = configGenerator.Generate(config) if err != nil { return packit.BuildResult{}, fmt.Errorf("failed to generate nginx.conf : %w", err) } } - confs, err := getIncludedConfs(buildEnv.ConfLocation) - if err != nil { - return packit.BuildResult{}, fmt.Errorf("failed to find configuration files: %w", err) + var hasNGINXConf bool + if _, err := os.Stat(config.NGINXConfLocation); err == nil { + hasNGINXConf = true } - for _, path := range append([]string{buildEnv.ConfLocation}, confs...) { - info, err := os.Stat(path) + if hasNGINXConf { + confs, err := getIncludedConfs(config.NGINXConfLocation) if err != nil { - return packit.BuildResult{}, fmt.Errorf("failed to stat configuration files: %w", err) + return packit.BuildResult{}, fmt.Errorf("failed to find configuration files: %w", err) } - err = os.Chmod(path, info.Mode()|0060) - if err != nil { - return packit.BuildResult{}, fmt.Errorf("failed to chmod configuration files: %w", err) + for _, path := range append([]string{config.NGINXConfLocation}, confs...) { + info, err := os.Stat(path) + if err != nil { + return packit.BuildResult{}, fmt.Errorf("failed to stat configuration files: %w", err) + } + + err = os.Chmod(path, info.Mode()|0060) + if err != nil { + return packit.BuildResult{}, fmt.Errorf("failed to chmod configuration files: %w", err) + } } } @@ -165,12 +128,11 @@ func Build(buildEnv BuildEnvironment, } var launchMetadata packit.LaunchMetadata - - if launch { + if launch && hasNGINXConf { command := "nginx" args := []string{ "-p", context.WorkingDir, - "-c", buildEnv.ConfLocation, + "-c", config.NGINXConfLocation, "-g", "pid /tmp/nginx.pid;", } launchMetadata.Processes = []packit.Process{ @@ -184,7 +146,7 @@ func Build(buildEnv BuildEnvironment, } launchMetadata.BOM = bom - if buildEnv.Reload { + if config.LiveReloadEnabled { launchMetadata.Processes = []packit.Process{ { Type: "web", @@ -254,21 +216,11 @@ func Build(buildEnv BuildEnvironment, logger.Break() layer.SharedEnv.Append("PATH", filepath.Join(layer.Path, "sbin"), string(os.PathListSeparator)) + layer.LaunchEnv.Default("EXECD_CONF", config.NGINXConfLocation) + layer.ExecD = []string{configureBinPath} - layer.LaunchEnv.Override("EXECD_CONF", buildEnv.ConfLocation) - execdDir := filepath.Join(layer.Path, "exec.d") - err = os.MkdirAll(execdDir, os.ModePerm) - if err != nil { - return packit.BuildResult{}, err - } - - err = fs.Copy(configureBinPath, filepath.Join(execdDir, "0-configure")) - if err != nil { - return packit.BuildResult{}, err - } - - if buildEnv.WebServer == "nginx" { - layer.LaunchEnv.Override("APP_ROOT", context.WorkingDir) + if config.WebServer == "nginx" { + layer.LaunchEnv.Default("APP_ROOT", context.WorkingDir) } logger.EnvironmentVariables(layer) diff --git a/build_test.go b/build_test.go index 166ab3f2..2f7e9ed9 100644 --- a/build_test.go +++ b/build_test.go @@ -3,7 +3,6 @@ package nginx_test import ( "bytes" "errors" - "fmt" "os" "path/filepath" "testing" @@ -13,7 +12,6 @@ import ( "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/chronos" "github.com/paketo-buildpacks/packit/v2/sbom" - "github.com/paketo-buildpacks/packit/v2/servicebindings" //nolint Ignore SA1019, informed usage of deprecated package "github.com/paketo-buildpacks/packit/v2/paketosbom" @@ -35,9 +33,8 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { entryResolver *fakes.EntryResolver dependencyService *fakes.DependencyService - config *fakes.ConfigGenerator + configGenerator *fakes.ConfigGenerator calculator *fakes.Calculator - bindings *fakes.Bindings sbomGenerator *fakes.SBOMGenerator buffer *bytes.Buffer @@ -93,21 +90,31 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { }, } - bindings = &fakes.Bindings{} - config = &fakes.ConfigGenerator{} + configGenerator = &fakes.ConfigGenerator{} calculator = &fakes.Calculator{} calculator.SumCall.Returns.String = "some-bin-sha" sbomGenerator = &fakes.SBOMGenerator{} sbomGenerator.GenerateFromDependencyCall.Returns.SBOM = sbom.SBOM{} - // create fake configure binary Expect(os.Mkdir(filepath.Join(cnbPath, "bin"), os.ModePerm)).To(Succeed()) Expect(os.WriteFile(filepath.Join(cnbPath, "bin", "configure"), []byte("binary-contents"), 0600)).To(Succeed()) Expect(os.WriteFile(filepath.Join(workspaceDir, "nginx.conf"), []byte("worker_processes 2;"), 0600)).To(Succeed()) - build = nginx.Build(nginx.BuildEnvironment{}, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) + build = nginx.Build( + nginx.Configuration{ + NGINXConfLocation: "./nginx.conf", + WebServerRoot: "./public", + }, + entryResolver, + dependencyService, + configGenerator, + calculator, + sbomGenerator, + scribe.NewEmitter(buffer), + chronos.DefaultClock, + ) }) it("does a build", func() { @@ -148,12 +155,13 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { "PATH.delim": ":", })) Expect(layer.LaunchEnv).To(Equal(packit.Environment{ - "EXECD_CONF.override": filepath.Join(workspaceDir, nginx.ConfFile), + "EXECD_CONF.default": filepath.Join(workspaceDir, nginx.ConfFile), })) Expect(layer.Metadata).To(Equal(map[string]interface{}{ nginx.DepKey: "some-sha", nginx.ConfigureBinKey: "some-bin-sha", })) + Expect(layer.ExecD).To(Equal([]string{filepath.Join(cnbPath, "bin", "configure")})) Expect(result.Launch.BOM).To(Equal([]packit.BOMEntry{ { @@ -195,7 +203,6 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { })) Expect(filepath.Join(layersDir, "nginx")).To(BeADirectory()) - Expect(filepath.Join(workspaceDir, "logs")).To(BeADirectory()) Expect(entryResolver.ResolveCall.Receives.BuildpackPlanEntrySlice).To(Equal([]packit.BuildpackPlanEntry{ { @@ -243,7 +250,20 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { context("when live reload is enabled", func() { it.Before(func() { - build = nginx.Build(nginx.BuildEnvironment{Reload: true}, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) + build = nginx.Build( + nginx.Configuration{ + NGINXConfLocation: "./nginx.conf", + WebServerRoot: "./public", + LiveReloadEnabled: true, + }, + entryResolver, + dependencyService, + configGenerator, + calculator, + sbomGenerator, + scribe.NewEmitter(buffer), + chronos.DefaultClock, + ) }) it("uses watchexec to set the start command", func() { @@ -391,7 +411,6 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { })) Expect(filepath.Join(layersDir, "nginx")).To(BeADirectory()) - Expect(filepath.Join(workspaceDir, "logs")).To(BeADirectory()) Expect(entryResolver.ResolveCall.Receives.BuildpackPlanEntrySlice).To(Equal([]packit.BuildpackPlanEntry{ { @@ -513,14 +532,19 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { context("when BP_NGINX_CONF_LOCATION is set to a relative path", func() { it.Before(func() { - buildEnv := nginx.BuildEnvironment{ - ConfLocation: "some-relative-path/nginx.conf", - } - Expect(os.Mkdir(filepath.Join(workspaceDir, "some-relative-path"), os.ModePerm)).To(Succeed()) Expect(os.WriteFile(filepath.Join(workspaceDir, "some-relative-path", "nginx.conf"), []byte("worker_processes 2;"), 0600)).To(Succeed()) - build = nginx.Build(buildEnv, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) + build = nginx.Build( + nginx.Configuration{NGINXConfLocation: "some-relative-path/nginx.conf"}, + entryResolver, + dependencyService, + configGenerator, + calculator, + sbomGenerator, + scribe.NewEmitter(buffer), + chronos.DefaultClock, + ) }) it("assumes path is relative to /workspace", func() { @@ -549,21 +573,26 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { "-g", "pid /tmp/nginx.pid;", })) Expect(result.Layers[0].LaunchEnv).To(Equal(packit.Environment{ - "EXECD_CONF.override": filepath.Join(workspaceDir, "some-relative-path/nginx.conf"), + "EXECD_CONF.default": filepath.Join(workspaceDir, "some-relative-path/nginx.conf"), })) }) }) context("when BP_NGINX_CONF_LOCATION is set to an absolute path", func() { it.Before(func() { - buildEnv := nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workspaceDir, "some-absolute-path", "nginx.conf"), - } - Expect(os.Mkdir(filepath.Join(workspaceDir, "some-absolute-path"), os.ModePerm)).To(Succeed()) Expect(os.WriteFile(filepath.Join(workspaceDir, "some-absolute-path", "nginx.conf"), []byte("worker_processes 2;"), 0600)).To(Succeed()) - build = nginx.Build(buildEnv, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) + build = nginx.Build( + nginx.Configuration{NGINXConfLocation: filepath.Join(workspaceDir, "some-absolute-path", "nginx.conf")}, + entryResolver, + dependencyService, + configGenerator, + calculator, + sbomGenerator, + scribe.NewEmitter(buffer), + chronos.DefaultClock, + ) }) it("uses the location as-is", func() { @@ -592,18 +621,27 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { "-g", "pid /tmp/nginx.pid;", })) Expect(result.Layers[0].LaunchEnv).To(Equal(packit.Environment{ - "EXECD_CONF.override": filepath.Join(workspaceDir, "some-absolute-path", "nginx.conf"), + "EXECD_CONF.default": filepath.Join(workspaceDir, "some-absolute-path", "nginx.conf"), })) }) }) context("when BP_WEB_SERVER=nginx in the build env", func() { it.Before(func() { - buildEnv := nginx.BuildEnvironment{ - WebServer: "nginx", - WebServerRoot: "custom", - } - build = nginx.Build(buildEnv, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) + build = nginx.Build( + nginx.Configuration{ + NGINXConfLocation: "./nginx.conf", + WebServer: "nginx", + WebServerRoot: "custom", + }, + entryResolver, + dependencyService, + configGenerator, + calculator, + sbomGenerator, + scribe.NewEmitter(buffer), + chronos.DefaultClock, + ) }) it("generates a basic nginx.conf and passes env var configuration into template generator", func() { @@ -627,64 +665,18 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { Layers: packit.Layers{Path: layersDir}, }) Expect(err).NotTo(HaveOccurred()) - Expect(config.GenerateCall.Receives.Env).To(Equal(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workspaceDir, "nginx.conf"), - WebServer: "nginx", - WebServerRoot: "custom", + Expect(configGenerator.GenerateCall.Receives.Config).To(Equal(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workspaceDir, "nginx.conf"), + WebServer: "nginx", + WebServerRoot: "custom", })) Expect(result.Layers[0].LaunchEnv).To(Equal(packit.Environment{ - "APP_ROOT.override": workspaceDir, // generated nginx conf relies on this env var - "EXECD_CONF.override": filepath.Join(workspaceDir, "nginx.conf"), + "APP_ROOT.default": workspaceDir, // generated nginx conf relies on this env var + "EXECD_CONF.default": filepath.Join(workspaceDir, "nginx.conf"), })) }) - context("and a well-formed htpasswd service binding is provided", func() { - it.Before(func() { - buildEnv := nginx.BuildEnvironment{ - WebServer: "nginx", - } - bindings.ResolveCall.Returns.BindingSlice = []servicebindings.Binding{ - { - Name: "first", - Type: "htpasswd", - Path: "/path/to/binding/", - Entries: map[string]*servicebindings.Entry{ - ".htpasswd": servicebindings.NewEntry("/path/to/binding/.htpasswd"), - }, - }, - } - build = nginx.Build(buildEnv, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) - }) - it("passes the binding path into the conf generator", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Stack: "some-stack", - Platform: packit.Platform{Path: "platform"}, - Plan: packit.BuildpackPlan{ - Entries: []packit.BuildpackPlanEntry{ - { - Name: "nginx", - Metadata: map[string]interface{}{ - "version-source": "BP_NGINX_VERSION", - "version": "1.19.*", - "launch": true, - }, - }, - }, - }, - Layers: packit.Layers{Path: layersDir}, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(config.GenerateCall.Receives.Env).To(Equal(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workspaceDir, nginx.ConfFile), - WebServer: "nginx", - BasicAuthFile: "/path/to/binding/.htpasswd", - })) - }) - }) - context("and nginx layer is being reused", func() { it.Before(func() { err := os.WriteFile(filepath.Join(layersDir, "nginx.toml"), []byte(`[metadata] @@ -721,10 +713,10 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { Layers: packit.Layers{Path: layersDir}, }) Expect(err).NotTo(HaveOccurred()) - Expect(config.GenerateCall.Receives.Env).To(Equal(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workspaceDir, "nginx.conf"), - WebServer: "nginx", - WebServerRoot: "custom", + Expect(configGenerator.GenerateCall.Receives.Config).To(Equal(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workspaceDir, "nginx.conf"), + WebServer: "nginx", + WebServerRoot: "custom", })) }) }) @@ -758,6 +750,28 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { }) }) + context("when there is no configuration file", func() { + it.Before(func() { + Expect(os.Remove(filepath.Join(workspaceDir, "nginx.conf"))).To(Succeed()) + }) + + it("does not attempt to set permissions or processes", func() { + result, err := build(packit.BuildContext{ + CNBPath: cnbPath, + WorkingDir: workspaceDir, + Stack: "some-stack", + Plan: packit.BuildpackPlan{ + Entries: []packit.BuildpackPlanEntry{{Name: "nginx"}}, + }, + Layers: packit.Layers{Path: layersDir}, + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Layers)).To(Equal(1)) + Expect(result.Launch.Processes).To(BeEmpty()) + }) + }) + context("failure cases", func() { context("when the dependency cannot be resolved", func() { it.Before(func() { @@ -779,112 +793,19 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { }) }) - context("unable to create log directory", func() { - it.Before(func() { - Expect(os.Chmod(workspaceDir, 0000)) - }) - - it.After(func() { - Expect(os.Chmod(workspaceDir, os.ModePerm)) - }) - - it("fails with descriptive error", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Stack: "some-stack", - }) - - Expect(err).To(HaveOccurred()) - logsDir := filepath.Join(workspaceDir, "logs") - Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("failed to create logs dir : mkdir %s", logsDir)))) - }) - }) - - context("unable to resolve .htpasswd service binding", func() { - it.Before(func() { - build = nginx.Build(nginx.BuildEnvironment{WebServer: "nginx"}, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) - bindings.ResolveCall.Returns.Error = errors.New("some bindings error") - }) - - it("fails with descriptive error", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Stack: "some-stack", - }) - - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("some bindings error"))) - }) - }) - - context("there's more than one htpasswd service binding", func() { - it.Before(func() { - bindings.ResolveCall.Returns.BindingSlice = []servicebindings.Binding{ - { - Name: "first", - Type: "htpasswd", - Path: "/path/to/binding/", - Entries: map[string]*servicebindings.Entry{ - ".htpasswd": servicebindings.NewEntry("/path/to/binding/.htpasswd"), - }, - }, - { - Name: "second", - Type: "htpasswd", - Path: "/path/to/binding/", - Entries: map[string]*servicebindings.Entry{ - ".htpasswd": servicebindings.NewEntry("/path/to/binding/.htpasswd"), - }, - }, - } - build = nginx.Build(nginx.BuildEnvironment{WebServer: "nginx"}, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) - }) - - it("fails with descriptive error", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Stack: "some-stack", - }) - - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("binding resolver found more than one binding of type 'htpasswd'"))) - }) - }) - - context("the htpasswd service binding is malformed", func() { - it.Before(func() { - bindings.ResolveCall.Returns.BindingSlice = []servicebindings.Binding{ - { - Name: "first", - Type: "htpasswd", - Path: "/path/to/binding/", - Entries: map[string]*servicebindings.Entry{ - "some-irrelevant-file": servicebindings.NewEntry("some-irrelevant-path"), - }, - }, - } - build = nginx.Build(nginx.BuildEnvironment{WebServer: "nginx"}, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) - }) - - it("fails with descriptive error", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Stack: "some-stack", - }) - - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("binding of type 'htpasswd' does not contain required entry '.htpasswd'"))) - }) - }) - context("unable to generate nginx.conf", func() { it.Before(func() { - build = nginx.Build(nginx.BuildEnvironment{WebServer: "nginx"}, entryResolver, dependencyService, bindings, config, calculator, sbomGenerator, scribe.NewEmitter(buffer), chronos.DefaultClock) - config.GenerateCall.Returns.Error = errors.New("some config error") + build = nginx.Build( + nginx.Configuration{WebServer: "nginx"}, + entryResolver, + dependencyService, + configGenerator, + calculator, + sbomGenerator, + scribe.NewEmitter(buffer), + chronos.DefaultClock, + ) + configGenerator.GenerateCall.Returns.Error = errors.New("some config error") }) it("fails with descriptive error", func() { @@ -986,49 +907,6 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { }) }) - context("when the exec.d binary cannot be copied", func() { - it.Before(func() { - Expect(os.Remove(filepath.Join(cnbPath, "bin", "configure"))).To(Succeed()) - }) - - it("returns an error", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Plan: packit.BuildpackPlan{ - Entries: []packit.BuildpackPlanEntry{ - {Name: "nginx"}, - }, - }, - Layers: packit.Layers{Path: layersDir}, - Stack: "some-stack", - }) - Expect(err).To(MatchError(ContainSubstring("no such file or directory"))) - }) - }) - - context("when the included confs cannot be fetched", func() { - it.Before(func() { - Expect(os.Remove(filepath.Join(workspaceDir, "nginx.conf"))).To(Succeed()) - }) - - it("returns an error", func() { - _, err := build(packit.BuildContext{ - CNBPath: cnbPath, - WorkingDir: workspaceDir, - Plan: packit.BuildpackPlan{ - Entries: []packit.BuildpackPlanEntry{ - {Name: "nginx"}, - }, - }, - Layers: packit.Layers{Path: layersDir}, - Stack: "some-stack", - }) - Expect(err).To(MatchError(ContainSubstring("failed to find configuration files"))) - Expect(err).To(MatchError(ContainSubstring("no such file or directory"))) - }) - }) - context("when generating the SBOM returns an error", func() { it.Before(func() { sbomGenerator.GenerateFromDependencyCall.Returns.Error = errors.New("failed to generate SBOM") diff --git a/cmd/configure/internal/run.go b/cmd/configure/internal/run.go index 85ce9d5a..317e693e 100644 --- a/cmd/configure/internal/run.go +++ b/cmd/configure/internal/run.go @@ -13,10 +13,15 @@ import ( func Run(mainConf, localModulePath, globalModulePath string) error { log.SetFlags(0) + if _, err := os.Stat(mainConf); err != nil { + return nil + } + confs, err := getIncludedConfs(mainConf) if err != nil { return err } + confs = append(confs, mainConf) templFuncs := template.FuncMap{ "env": os.Getenv, diff --git a/cmd/configure/internal/run_test.go b/cmd/configure/internal/run_test.go index d686803c..5e3bafee 100644 --- a/cmd/configure/internal/run_test.go +++ b/cmd/configure/internal/run_test.go @@ -32,8 +32,9 @@ func testRun(t *testing.T, context spec.G, it spec.S) { it.Before(func() { var err error workingDir, err = os.MkdirTemp("", "working-dir") - mainConf = filepath.Join(workingDir, "nginx.conf") Expect(err).NotTo(HaveOccurred()) + + mainConf = filepath.Join(workingDir, "nginx.conf") }) it.After(func() { @@ -198,18 +199,18 @@ func testRun(t *testing.T, context spec.G, it spec.S) { }) }) - context("failure cases", func() { - context("when the template file does not exist", func() { - it.Before(func() { - mainConf = "/no/such/template.conf" - }) + context("when the template file does not exist", func() { + it.Before(func() { + mainConf = "/no/such/nginx.conf" + }) - it("prints an error and exits non-zero", func() { - err := internal.Run(mainConf, localModulePath, globalModulePath) - Expect(err).To(MatchError(ContainSubstring("could not read config file (/no/such/template.conf): open /no/such/template.conf: no such file or directory"))) - }) + it("does nothing", func() { + err := internal.Run(mainConf, localModulePath, globalModulePath) + Expect(err).NotTo(HaveOccurred()) }) + }) + context("failure cases", func() { context("when the template file cannot be written", func() { it.Before(func() { Expect(os.WriteFile(filepath.Join(workingDir, "nginx.conf"), []byte(`{{module "global"}}`), 0444)).To(Succeed()) diff --git a/configuration.go b/configuration.go new file mode 100644 index 00000000..c0cc60db --- /dev/null +++ b/configuration.go @@ -0,0 +1,63 @@ +package nginx + +import ( + "errors" + "fmt" + "path/filepath" + "strings" + + "github.com/Netflix/go-env" + "github.com/paketo-buildpacks/packit/v2/servicebindings" +) + +//go:generate faux --interface BindingsResolver --output fakes/bindings_resolver.go +type BindingsResolver interface { + ResolveOne(typ, provider, platformDir string) (servicebindings.Binding, error) +} + +type Configuration struct { + NGINXConfLocation string `env:"BP_NGINX_CONF_LOCATION"` + NGINXVersion string `env:"BP_NGINX_VERSION"` + LiveReloadEnabled bool `env:"BP_LIVE_RELOAD_ENABLED"` + WebServer string `env:"BP_WEB_SERVER"` + WebServerForceHTTPS bool `env:"BP_WEB_SERVER_FORCE_HTTPS"` + WebServerEnablePushState bool `env:"BP_WEB_SERVER_ENABLE_PUSH_STATE"` + WebServerRoot string `env:"BP_WEB_SERVER_ROOT"` + WebServerLocationPath string `env:"BP_WEB_SERVER_LOCATION_PATH"` + + BasicAuthFile string +} + +func LoadConfiguration(environ []string, bindingsResolver BindingsResolver, platformPath string) (Configuration, error) { + es, err := env.EnvironToEnvSet(environ) + if err != nil { + return Configuration{}, fmt.Errorf("failed to parse environment variables: %w", err) + } + + configuration := Configuration{ + NGINXConfLocation: "./nginx.conf", + WebServerRoot: "./public", + } + + err = env.Unmarshal(es, &configuration) + if err != nil { + return Configuration{}, err + } + + if configuration.WebServer == "nginx" { + binding, err := bindingsResolver.ResolveOne("htpasswd", "", platformPath) + if err != nil && !strings.Contains(err.Error(), "expected exactly 1") { + return Configuration{}, err + } + + if err == nil { + if _, ok := binding.Entries[".htpasswd"]; !ok { + return Configuration{}, errors.New("binding of type 'htpasswd' does not contain required entry '.htpasswd'") + } + + configuration.BasicAuthFile = filepath.Join(binding.Path, ".htpasswd") + } + } + + return configuration, nil +} diff --git a/configuration_test.go b/configuration_test.go new file mode 100644 index 00000000..10b695d0 --- /dev/null +++ b/configuration_test.go @@ -0,0 +1,135 @@ +package nginx_test + +import ( + "errors" + "testing" + + "github.com/paketo-buildpacks/nginx" + "github.com/paketo-buildpacks/nginx/fakes" + "github.com/paketo-buildpacks/packit/v2/servicebindings" + "github.com/sclevine/spec" + + . "github.com/onsi/gomega" +) + +func testConfiguration(t *testing.T, context spec.G, it spec.S) { + var Expect = NewWithT(t).Expect + + context("LoadConfiguration", func() { + var bindingsResolver *fakes.BindingsResolver + + it.Before(func() { + bindingsResolver = &fakes.BindingsResolver{} + bindingsResolver.ResolveOneCall.Returns.Binding = servicebindings.Binding{ + Name: "first", + Type: "htpasswd", + Path: "/path/to/binding/", + Entries: map[string]*servicebindings.Entry{ + ".htpasswd": servicebindings.NewEntry("/path/to/binding/.htpasswd"), + }, + } + }) + + it("loads the buildpack configuration", func() { + config, err := nginx.LoadConfiguration([]string{ + "BP_NGINX_CONF_LOCATION=some-conf-location", + "BP_NGINX_VERSION=some-nginx-version", + "BP_LIVE_RELOAD_ENABLED=true", + "BP_WEB_SERVER=some-web-server", + "BP_WEB_SERVER_FORCE_HTTPS=true", + "BP_WEB_SERVER_ENABLE_PUSH_STATE=true", + "BP_WEB_SERVER_ROOT=some-root", + "BP_WEB_SERVER_LOCATION_PATH=some-location-path", + }, bindingsResolver, "some-platform-path") + Expect(err).NotTo(HaveOccurred()) + Expect(config).To(Equal(nginx.Configuration{ + NGINXConfLocation: "some-conf-location", + NGINXVersion: "some-nginx-version", + LiveReloadEnabled: true, + WebServer: "some-web-server", + WebServerForceHTTPS: true, + WebServerEnablePushState: true, + WebServerRoot: "some-root", + WebServerLocationPath: "some-location-path", + })) + }) + + context("when no BP_NGINX_CONF_LOCATION is set", func() { + it("assigns a default", func() { + config, err := nginx.LoadConfiguration(nil, bindingsResolver, "some-platform-path") + Expect(err).NotTo(HaveOccurred()) + Expect(config.NGINXConfLocation).To(Equal("./nginx.conf")) + }) + }) + + context("when no BP_WEB_SERVER_ROOT is set", func() { + it("assigns a default", func() { + config, err := nginx.LoadConfiguration(nil, bindingsResolver, "some-platform-path") + Expect(err).NotTo(HaveOccurred()) + Expect(config.WebServerRoot).To(Equal("./public")) + }) + }) + + context("when BP_WEB_SERVER=nginx", func() { + context("when a .htpasswd service binding is provided", func() { + it("loads the binding path", func() { + config, err := nginx.LoadConfiguration([]string{"BP_WEB_SERVER=nginx"}, bindingsResolver, "some-platform-path") + Expect(err).NotTo(HaveOccurred()) + Expect(config.BasicAuthFile).To(Equal("/path/to/binding/.htpasswd")) + }) + }) + + context("when a .htpasswd service binding is NOT provided", func() { + it.Before(func() { + bindingsResolver.ResolveOneCall.Returns.Error = errors.New("expected exactly 1") + }) + + it("does not load the binding path", func() { + config, err := nginx.LoadConfiguration([]string{"BP_WEB_SERVER=nginx"}, bindingsResolver, "some-platform-path") + Expect(err).NotTo(HaveOccurred()) + Expect(config.BasicAuthFile).To(Equal("")) + }) + }) + }) + + context("failure cases", func() { + context("when the environment cannot be parsed", func() { + it("returns an error", func() { + _, err := nginx.LoadConfiguration([]string{ + "this is not a parseable environment variable", + }, bindingsResolver, "some-platform-path") + Expect(err).To(MatchError("failed to parse environment variables: items in environ must have format key=value")) + }) + }) + + context("when resolving the .htpasswd service binding fails", func() { + it.Before(func() { + bindingsResolver.ResolveOneCall.Returns.Error = errors.New("some bindings error") + }) + + it("returns an error", func() { + _, err := nginx.LoadConfiguration([]string{"BP_WEB_SERVER=nginx"}, bindingsResolver, "some-platform-path") + Expect(err).To(MatchError(ContainSubstring("some bindings error"))) + }) + }) + + context("when the .htpasswd service binding is malformed", func() { + it.Before(func() { + bindingsResolver.ResolveOneCall.Returns.Binding = servicebindings.Binding{ + Name: "first", + Type: "htpasswd", + Path: "/path/to/binding/", + Entries: map[string]*servicebindings.Entry{ + "some-irrelevant-file": servicebindings.NewEntry("some-irrelevant-path"), + }, + } + }) + + it("returns an error", func() { + _, err := nginx.LoadConfiguration([]string{"BP_WEB_SERVER=nginx"}, bindingsResolver, "some-platform-path") + Expect(err).To(MatchError("binding of type 'htpasswd' does not contain required entry '.htpasswd'")) + }) + }) + }) + }) +} diff --git a/default_config_generator.go b/default_config_generator.go index af578027..8ad54ead 100644 --- a/default_config_generator.go +++ b/default_config_generator.go @@ -2,6 +2,7 @@ package nginx import ( "bytes" + _ "embed" "fmt" "io" "os" @@ -11,60 +12,57 @@ import ( "github.com/paketo-buildpacks/packit/v2/scribe" ) +//go:embed assets/default.conf +var DefaultConfigTemplate string + type DefaultConfigGenerator struct { logs scribe.Emitter } func NewDefaultConfigGenerator(logs scribe.Emitter) DefaultConfigGenerator { - return DefaultConfigGenerator{ - logs: logs, - } + return DefaultConfigGenerator{logs: logs} } -func (g DefaultConfigGenerator) Generate(env BuildEnvironment) error { - g.logs.Process("Generating %s", env.ConfLocation) - t := template.Must(template.New("template.conf").Delims("$((", "))").Parse(defaultConf)) - - if env.WebServerRoot == "" { - env.WebServerRoot = `./public` - } +func (g DefaultConfigGenerator) Generate(config Configuration) error { + g.logs.Process("Generating %s", config.NGINXConfLocation) + t := template.Must(template.New("template.conf").Delims("$((", "))").Parse(DefaultConfigTemplate)) - if !filepath.IsAbs(env.WebServerRoot) { - env.WebServerRoot = filepath.Join(`{{ env "APP_ROOT" }}`, env.WebServerRoot) + if !filepath.IsAbs(config.WebServerRoot) { + config.WebServerRoot = filepath.Join(`{{ env "APP_ROOT" }}`, config.WebServerRoot) } - g.logs.Subprocess("Setting server root directory to '%s'", env.WebServerRoot) + g.logs.Subprocess("Setting server root directory to '%s'", config.WebServerRoot) - if env.WebServerLocationPath == "" { - env.WebServerLocationPath = "/" + if config.WebServerLocationPath == "" { + config.WebServerLocationPath = "/" } - g.logs.Subprocess("Setting server location path to '%s'", env.WebServerLocationPath) + g.logs.Subprocess("Setting server location path to '%s'", config.WebServerLocationPath) - if env.WebServerPushStateEnabled { + if config.WebServerEnablePushState { g.logs.Subprocess("Enabling push state routing") } - if env.WebServerForceHTTPS { + if config.WebServerForceHTTPS { g.logs.Subprocess("Setting server to redirect HTTP requests to HTTPS") } - if env.BasicAuthFile != "" { + if config.BasicAuthFile != "" { g.logs.Subprocess("Enabling basic authentication with .htpasswd credentials") } g.logs.Break() var b bytes.Buffer - err := t.Execute(&b, env) + err := t.Execute(&b, config) if err != nil { // not tested return err } - f, err := os.OpenFile(env.ConfLocation, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + f, err := os.OpenFile(config.NGINXConfLocation, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { - return fmt.Errorf("failed to create %s: %w", env.ConfLocation, err) + return fmt.Errorf("failed to create %s: %w", config.NGINXConfLocation, err) } defer f.Close() diff --git a/default_config_generator_test.go b/default_config_generator_test.go index 2f98779a..899a9235 100644 --- a/default_config_generator_test.go +++ b/default_config_generator_test.go @@ -36,8 +36,9 @@ func testDefaultConfigGenerator(t *testing.T, context spec.G, it spec.S) { context("Generate", func() { it("writes a default nginx.conf to the working directory", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), + WebServerRoot: "./public", }) Expect(err).NotTo(HaveOccurred()) @@ -223,9 +224,9 @@ error_log stderr; }) it("writes a nginx.conf with specified relative root directory", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), - WebServerRoot: "custom", + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), + WebServerRoot: "custom", }) Expect(err).NotTo(HaveOccurred()) @@ -236,9 +237,9 @@ error_log stderr; }) it("writes a nginx.conf with specified absolute path to root directory", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), - WebServerRoot: "/some/absolute/path", + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), + WebServerRoot: "/some/absolute/path", }) Expect(err).NotTo(HaveOccurred()) @@ -249,8 +250,8 @@ error_log stderr; }) it("writes a nginx.conf with specified location path", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), WebServerLocationPath: "/path", }) Expect(err).NotTo(HaveOccurred()) @@ -262,9 +263,9 @@ error_log stderr; }) it("writes an nginx.conf that conditionally includes the PushState content", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), - WebServerPushStateEnabled: true, + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), + WebServerEnablePushState: true, }) Expect(err).NotTo(HaveOccurred()) @@ -280,9 +281,10 @@ error_log stderr; }) it("writes an nginx.conf that conditionally includes the Force HTTPS content", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), WebServerForceHTTPS: true, + WebServerRoot: "./public", }) Expect(err).NotTo(HaveOccurred()) @@ -304,9 +306,10 @@ error_log stderr; }) it("writes an nginx.conf that conditionally includes the Basic Auth content", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), - BasicAuthFile: "/some/file/path", + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), + WebServerRoot: "./public", + BasicAuthFile: "/some/file/path", }) Expect(err).NotTo(HaveOccurred()) @@ -327,8 +330,8 @@ error_log stderr; Expect(os.WriteFile(filepath.Join(workingDir, "nginx.conf"), []byte("read-only file"), 0444)).To(Succeed()) }) it("returns an error", func() { - err := generator.Generate(nginx.BuildEnvironment{ - ConfLocation: filepath.Join(workingDir, "nginx.conf"), + err := generator.Generate(nginx.Configuration{ + NGINXConfLocation: filepath.Join(workingDir, "nginx.conf"), }) Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("failed to create %[1]s: open %[1]s: permission denied", filepath.Join(workingDir, "nginx.conf"))))) }) diff --git a/detect.go b/detect.go index cad7794f..84001ec0 100644 --- a/detect.go +++ b/detect.go @@ -20,7 +20,7 @@ type BuildPlanMetadata struct { Launch bool `toml:"launch"` } -func Detect(buildEnv BuildEnvironment, versionParser VersionParser) packit.DetectFunc { +func Detect(config Configuration, versionParser VersionParser) packit.DetectFunc { return func(context packit.DetectContext) (packit.DetectResult, error) { plan := packit.DetectResult{ Plan: packit.BuildPlan{ @@ -30,18 +30,22 @@ func Detect(buildEnv BuildEnvironment, versionParser VersionParser) packit.Detec }, } - confExists, err := fs.Exists(cleanNginxConfLocation(buildEnv.ConfLocation, context.WorkingDir)) + if !filepath.IsAbs(config.NGINXConfLocation) { + config.NGINXConfLocation = filepath.Join(context.WorkingDir, config.NGINXConfLocation) + } + + confExists, err := fs.Exists(config.NGINXConfLocation) if err != nil { return packit.DetectResult{}, fmt.Errorf("failed to stat nginx.conf: %w", err) } - if !confExists && buildEnv.WebServer != "nginx" { + if !confExists && config.WebServer != "nginx" { return plan, nil } var requirements []packit.BuildPlanRequirement var version string - if buildEnv.NginxVersion != "" { - version, err = versionParser.ResolveVersion(context.CNBPath, buildEnv.NginxVersion) + if config.NGINXVersion != "" { + version, err = versionParser.ResolveVersion(context.CNBPath, config.NGINXVersion) if err != nil { return packit.DetectResult{}, err } @@ -72,7 +76,7 @@ func Detect(buildEnv BuildEnvironment, versionParser VersionParser) packit.Detec }) } - if buildEnv.NginxVersion == "" && !ymlExists { + if config.NGINXVersion == "" && !ymlExists { version, err = versionParser.ResolveVersion(context.CNBPath, "") if err != nil { return packit.DetectResult{}, err @@ -91,7 +95,7 @@ func Detect(buildEnv BuildEnvironment, versionParser VersionParser) packit.Detec return packit.DetectResult{}, fmt.Errorf("parsing version failed: %w", err) } - if buildEnv.Reload { + if config.LiveReloadEnabled { requirements = append(requirements, packit.BuildPlanRequirement{ Name: "watchexec", Metadata: map[string]interface{}{ @@ -105,13 +109,3 @@ func Detect(buildEnv BuildEnvironment, versionParser VersionParser) packit.Detec return plan, nil } } - -func cleanNginxConfLocation(confLocation, workingDir string) string { - if confLocation != "" { - if filepath.IsAbs(confLocation) { - return confLocation - } - return filepath.Join(workingDir, confLocation) - } - return filepath.Join(workingDir, ConfFile) -} diff --git a/detect_test.go b/detect_test.go index 81ca9991..d3c739b7 100644 --- a/detect_test.go +++ b/detect_test.go @@ -36,7 +36,7 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { versionParser = &fakes.VersionParser{} versionParser.ResolveVersionCall.Returns.ResultVersion = "1.19.*" - detect = nginx.Detect(nginx.BuildEnvironment{}, versionParser) + detect = nginx.Detect(nginx.Configuration{NGINXConfLocation: "./nginx.conf", WebServerRoot: "./public"}, versionParser) }) it.After(func() { @@ -58,8 +58,9 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { context("$BP_WEB_SERVER is set for a no-config nginx build", func() { it.Before(func() { - detect = nginx.Detect(nginx.BuildEnvironment{WebServer: "nginx"}, versionParser) + detect = nginx.Detect(nginx.Configuration{WebServer: "nginx"}, versionParser) }) + it("requires and provides nginx", func() { result, err := detect(packit.DetectContext{ WorkingDir: workingDir, @@ -86,15 +87,12 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { context("nginx.conf is present", func() { it.Before(func() { - Expect(os.WriteFile(filepath.Join(workingDir, "nginx.conf"), - []byte(`conf`), - 0644, - )).To(Succeed()) + Expect(os.WriteFile(filepath.Join(workingDir, "nginx.conf"), []byte(`conf`), 0600)).To(Succeed()) }) context("when version is set via BP_NGINX_VERSION", func() { it.Before(func() { - detect = nginx.Detect(nginx.BuildEnvironment{NginxVersion: "mainline"}, versionParser) + detect = nginx.Detect(nginx.Configuration{NGINXVersion: "mainline"}, versionParser) versionParser.ResolveVersionCall.Returns.ResultVersion = "1.19.*" versionParser.ResolveVersionCall.Returns.Err = nil }) @@ -129,7 +127,7 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { context("and BP_LIVE_RELOAD_ENABLED=true in the build environment", func() { it.Before(func() { - detect = nginx.Detect(nginx.BuildEnvironment{Reload: true}, versionParser) + detect = nginx.Detect(nginx.Configuration{LiveReloadEnabled: true}, versionParser) }) it("requires watchexec at launch time", func() { diff --git a/fakes/binding_resolver.go b/fakes/binding_resolver.go deleted file mode 100644 index ff3bc1a1..00000000 --- a/fakes/binding_resolver.go +++ /dev/null @@ -1,37 +0,0 @@ -package fakes - -import ( - "sync" - - "github.com/paketo-buildpacks/packit/v2/servicebindings" -) - -type Bindings struct { - ResolveCall struct { - mutex sync.Mutex - CallCount int - Receives struct { - Typ string - Provider string - PlatformDir string - } - Returns struct { - BindingSlice []servicebindings.Binding - Error error - } - Stub func(string, string, string) ([]servicebindings.Binding, error) - } -} - -func (f *Bindings) Resolve(param1 string, param2 string, param3 string) ([]servicebindings.Binding, error) { - f.ResolveCall.mutex.Lock() - defer f.ResolveCall.mutex.Unlock() - f.ResolveCall.CallCount++ - f.ResolveCall.Receives.Typ = param1 - f.ResolveCall.Receives.Provider = param2 - f.ResolveCall.Receives.PlatformDir = param3 - if f.ResolveCall.Stub != nil { - return f.ResolveCall.Stub(param1, param2, param3) - } - return f.ResolveCall.Returns.BindingSlice, f.ResolveCall.Returns.Error -} diff --git a/fakes/bindings_resolver.go b/fakes/bindings_resolver.go new file mode 100644 index 00000000..bb7e0a93 --- /dev/null +++ b/fakes/bindings_resolver.go @@ -0,0 +1,37 @@ +package fakes + +import ( + "sync" + + "github.com/paketo-buildpacks/packit/v2/servicebindings" +) + +type BindingsResolver struct { + ResolveOneCall struct { + mutex sync.Mutex + CallCount int + Receives struct { + Typ string + Provider string + PlatformDir string + } + Returns struct { + Binding servicebindings.Binding + Error error + } + Stub func(string, string, string) (servicebindings.Binding, error) + } +} + +func (f *BindingsResolver) ResolveOne(param1 string, param2 string, param3 string) (servicebindings.Binding, error) { + f.ResolveOneCall.mutex.Lock() + defer f.ResolveOneCall.mutex.Unlock() + f.ResolveOneCall.CallCount++ + f.ResolveOneCall.Receives.Typ = param1 + f.ResolveOneCall.Receives.Provider = param2 + f.ResolveOneCall.Receives.PlatformDir = param3 + if f.ResolveOneCall.Stub != nil { + return f.ResolveOneCall.Stub(param1, param2, param3) + } + return f.ResolveOneCall.Returns.Binding, f.ResolveOneCall.Returns.Error +} diff --git a/fakes/build_environment_parser.go b/fakes/build_environment_parser.go deleted file mode 100644 index bfbf7a20..00000000 --- a/fakes/build_environment_parser.go +++ /dev/null @@ -1,29 +0,0 @@ -package fakes - -import ( - "sync" - - "github.com/paketo-buildpacks/nginx" -) - -type BuildEnvironmentParser struct { - ParseCall struct { - mutex sync.Mutex - CallCount int - Returns struct { - BuildEnvironment nginx.BuildEnvironment - Error error - } - Stub func() (nginx.BuildEnvironment, error) - } -} - -func (f *BuildEnvironmentParser) Parse() (nginx.BuildEnvironment, error) { - f.ParseCall.mutex.Lock() - defer f.ParseCall.mutex.Unlock() - f.ParseCall.CallCount++ - if f.ParseCall.Stub != nil { - return f.ParseCall.Stub() - } - return f.ParseCall.Returns.BuildEnvironment, f.ParseCall.Returns.Error -} diff --git a/fakes/config_generator.go b/fakes/config_generator.go index 1a585538..2d2c442f 100644 --- a/fakes/config_generator.go +++ b/fakes/config_generator.go @@ -11,20 +11,20 @@ type ConfigGenerator struct { mutex sync.Mutex CallCount int Receives struct { - Env nginx.BuildEnvironment + Config nginx.Configuration } Returns struct { Error error } - Stub func(nginx.BuildEnvironment) error + Stub func(nginx.Configuration) error } } -func (f *ConfigGenerator) Generate(param1 nginx.BuildEnvironment) error { +func (f *ConfigGenerator) Generate(param1 nginx.Configuration) error { f.GenerateCall.mutex.Lock() defer f.GenerateCall.mutex.Unlock() f.GenerateCall.CallCount++ - f.GenerateCall.Receives.Env = param1 + f.GenerateCall.Receives.Config = param1 if f.GenerateCall.Stub != nil { return f.GenerateCall.Stub(param1) } diff --git a/go.mod b/go.mod index 58b44ff4..b79293fe 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/BurntSushi/toml v1.1.0 github.com/Masterminds/semver v1.5.0 - github.com/caarlos0/env/v6 v6.9.3 + github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d github.com/onsi/gomega v1.19.0 github.com/paketo-buildpacks/occam v0.9.0 github.com/paketo-buildpacks/packit/v2 v2.3.1 diff --git a/go.sum b/go.sum index 0ad6696a..e5996033 100644 --- a/go.sum +++ b/go.sum @@ -232,6 +232,8 @@ github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5 github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d h1:wvStE9wLpws31NiWUx+38wny1msZ/tm+eL5xmm4Y7So= +github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= @@ -452,8 +454,6 @@ github.com/bytecodealliance/wasmtime-go v0.31.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOE github.com/bytecodealliance/wasmtime-go v0.33.1/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= -github.com/caarlos0/env/v6 v6.9.3 h1:Tyg69hoVXDnpO5Qvpsu8EoquarbPyQb+YwExWHP8wWU= -github.com/caarlos0/env/v6 v6.9.3/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/carolynvs/magex v0.6.0/go.mod h1:hqaEkr9TAv+kFb/5wgDiTdszF13rpe0Q+bWHmTe6N74= github.com/carolynvs/magex v0.7.0/go.mod h1:vZB3BkRfkd5ZMtkxJkCGbdFyWGoZiuNPKhx6uEQARmY= diff --git a/init_test.go b/init_test.go index 1ee8bc0d..010d5193 100644 --- a/init_test.go +++ b/init_test.go @@ -12,8 +12,9 @@ func TestUnitNGINX(t *testing.T) { format.MaxLength = 0 suite := spec.New("nginx", spec.Report(report.Terminal{})) suite("Build", testBuild) + suite("Configuration", testConfiguration) + suite("DefaultConfigGenerator", testDefaultConfigGenerator) suite("Detect", testDetect) suite("Parse", testParser) - suite("DefaultConfigGenerator", testDefaultConfigGenerator) suite.Run(t) } diff --git a/integration.json b/integration.json index 13362f54..4869fdc5 100644 --- a/integration.json +++ b/integration.json @@ -1,3 +1,4 @@ { + "build-plan": "github.com/paketo-community/build-plan", "watchexec": "github.com/paketo-buildpacks/watchexec" } diff --git a/integration/caching_test.go b/integration/caching_test.go index 2dc07ca1..09aba96f 100644 --- a/integration/caching_test.go +++ b/integration/caching_test.go @@ -55,7 +55,7 @@ func testCaching(t *testing.T, context spec.G, it spec.S) { }) it("uses a cached layer and doesn't run twice", func() { - build := pack.Build.WithBuildpacks(nginxBuildpack) + build := pack.Build.WithBuildpacks(settings.Buildpacks.NGINX.Online) firstImage, _, err := build.Execute(name, source) Expect(err).NotTo(HaveOccurred()) @@ -63,7 +63,7 @@ func testCaching(t *testing.T, context spec.G, it spec.S) { imageIDs[firstImage.ID] = struct{}{} Expect(firstImage.Buildpacks).To(HaveLen(1)) - Expect(firstImage.Buildpacks[0].Key).To(Equal(buildpackInfo.Buildpack.ID)) + Expect(firstImage.Buildpacks[0].Key).To(Equal(settings.Buildpack.ID)) Expect(firstImage.Buildpacks[0].Layers).To(HaveKey("nginx")) container, err := docker.Container.Run. @@ -82,7 +82,7 @@ func testCaching(t *testing.T, context spec.G, it spec.S) { imageIDs[secondImage.ID] = struct{}{} Expect(secondImage.Buildpacks).To(HaveLen(1)) - Expect(secondImage.Buildpacks[0].Key).To(Equal(buildpackInfo.Buildpack.ID)) + Expect(secondImage.Buildpacks[0].Key).To(Equal(settings.Buildpack.ID)) Expect(secondImage.Buildpacks[0].Layers).To(HaveKey("nginx")) container, err = docker.Container.Run. diff --git a/integration/custom_conf_app_test.go b/integration/custom_conf_app_test.go index bf4ae860..bf55b02f 100644 --- a/integration/custom_conf_app_test.go +++ b/integration/custom_conf_app_test.go @@ -54,7 +54,7 @@ func testCustomConfApp(t *testing.T, context spec.G, it spec.S) { it("serves up staticfile", func() { var err error image, _, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithPullPolicy("never"). WithEnv(map[string]string{ "BP_NGINX_CONF_LOCATION": "conf/nginx.conf", diff --git a/integration/init_test.go b/integration/init_test.go index 19dd5cfd..98a15146 100644 --- a/integration/init_test.go +++ b/integration/init_test.go @@ -17,17 +17,30 @@ import ( . "github.com/onsi/gomega" ) -var ( - nginxBuildpack string - offlineNginxBuildpack string - watchexecBuildpack string - buildpackInfo struct { - Buildpack struct { - ID string - Name string +var settings struct { + Buildpacks struct { + NGINX struct { + Online string + Offline string + } + Watchexec struct { + Online string + } + BuildPlan struct { + Online string } } -) + + Buildpack struct { + ID string + Name string + } + + Config struct { + Watchexec string `json:"watchexec"` + BuildPlan string `json:"build-plan"` + } +} func TestIntegration(t *testing.T) { format.MaxLength = 0 @@ -40,44 +53,46 @@ func TestIntegration(t *testing.T) { Expect(err).NotTo(HaveOccurred()) defer file.Close() - _, err = toml.NewDecoder(file).Decode(&buildpackInfo) + _, err = toml.NewDecoder(file).Decode(&settings) Expect(err).NotTo(HaveOccurred()) file, err = os.Open("../integration.json") Expect(err).NotTo(HaveOccurred()) defer file.Close() - var config struct { - Watchexec string `json:"watchexec"` - } - Expect(json.NewDecoder(file).Decode(&config)).To(Succeed()) + Expect(json.NewDecoder(file).Decode(&settings.Config)).To(Succeed()) buildpackStore := occam.NewBuildpackStore() libpakBuildpackStore := occam.NewBuildpackStore().WithPackager(packagers.NewLibpak()) - nginxBuildpack, err = buildpackStore.Get. + settings.Buildpacks.NGINX.Online, err = buildpackStore.Get. WithVersion("1.2.3"). Execute(root) Expect(err).NotTo(HaveOccurred()) - offlineNginxBuildpack, err = buildpackStore.Get. + settings.Buildpacks.NGINX.Offline, err = buildpackStore.Get. WithOfflineDependencies(). WithVersion("1.2.3"). Execute(root) Expect(err).NotTo(HaveOccurred()) - watchexecBuildpack, err = libpakBuildpackStore.Get. - Execute(config.Watchexec) + settings.Buildpacks.Watchexec.Online, err = libpakBuildpackStore.Get. + Execute(settings.Config.Watchexec) + Expect(err).ToNot(HaveOccurred()) + + settings.Buildpacks.BuildPlan.Online, err = libpakBuildpackStore.Get. + Execute(settings.Config.BuildPlan) Expect(err).ToNot(HaveOccurred()) SetDefaultEventuallyTimeout(5 * time.Second) suite := spec.New("Integration", spec.Report(report.Terminal{}), spec.Parallel()) + suite("Require", testRequire) suite("Caching", testCaching) + suite("CustomConfApp", testCustomConfApp) suite("Logging", testLogging) suite("NoConfApp", testNoConfApp) suite("Offline", testOffline) suite("SimpleApp", testSimpleApp) - suite("CustomConfApp", testCustomConfApp) suite.Run(t) } diff --git a/integration/logging_test.go b/integration/logging_test.go index d725ab03..7d715c76 100644 --- a/integration/logging_test.go +++ b/integration/logging_test.go @@ -49,13 +49,13 @@ func testLogging(t *testing.T, context spec.G, it spec.S) { var err error var logs fmt.Stringer image, logs, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithPullPolicy("never"). Execute(name, source) Expect(err).NotTo(HaveOccurred()) Expect(logs).To(matchers.ContainLines( - fmt.Sprintf("%s 1.2.3", buildpackInfo.Buildpack.Name), + fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), " Resolving Nginx Server version", " Candidate version sources (in priority order):", ` buildpack.yml -> "1.21.*"`, @@ -70,11 +70,11 @@ func testLogging(t *testing.T, context spec.G, it spec.S) { MatchRegexp(` Completed in (\d+\.\d+|\d{3})`), "", " Configuring build environment", - fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")), + fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(settings.Buildpack.ID, "/", "_")), "", " Configuring launch environment", ` EXECD_CONF -> "/workspace/nginx.conf"`, - fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")), + fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(settings.Buildpack.ID, "/", "_")), "", )) }) @@ -86,13 +86,13 @@ func testLogging(t *testing.T, context spec.G, it spec.S) { var logs fmt.Stringer image, logs, err = pack.Build. WithEnv(map[string]string{"BP_NGINX_VERSION": "stable"}). - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithPullPolicy("never"). Execute(name, source) Expect(err).NotTo(HaveOccurred()) Expect(logs).To(matchers.ContainLines( - fmt.Sprintf("%s 1.2.3", buildpackInfo.Buildpack.Name), + fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), " Resolving Nginx Server version", " Candidate version sources (in priority order):", ` BP_NGINX_VERSION -> "1.22.*"`, @@ -105,11 +105,11 @@ func testLogging(t *testing.T, context spec.G, it spec.S) { MatchRegexp(` Completed in (\d+\.\d+|\d{3})`), "", " Configuring build environment", - fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")), + fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(settings.Buildpack.ID, "/", "_")), "", " Configuring launch environment", ` EXECD_CONF -> "/workspace/nginx.conf"`, - fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")), + fmt.Sprintf(` PATH -> "$PATH:/layers/%s/nginx/sbin"`, strings.ReplaceAll(settings.Buildpack.ID, "/", "_")), "", )) }) diff --git a/integration/no_conf_app_test.go b/integration/no_conf_app_test.go index f3edb7b1..cc694839 100644 --- a/integration/no_conf_app_test.go +++ b/integration/no_conf_app_test.go @@ -58,7 +58,7 @@ func testNoConfApp(t *testing.T, context spec.G, it spec.S) { ) image, logs, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithEnv(map[string]string{ "BP_WEB_SERVER": "nginx", }). @@ -96,7 +96,7 @@ func testNoConfApp(t *testing.T, context spec.G, it spec.S) { ) image, logs, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithEnv(map[string]string{ "BP_WEB_SERVER": "nginx", "BP_WEB_SERVER_ROOT": "custom_root", @@ -134,7 +134,7 @@ func testNoConfApp(t *testing.T, context spec.G, it spec.S) { ) image, logs, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithEnv(map[string]string{ "BP_WEB_SERVER": "nginx", "BP_WEB_SERVER_FORCE_HTTPS": "true", @@ -188,7 +188,7 @@ func testNoConfApp(t *testing.T, context spec.G, it spec.S) { ) image, logs, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithEnv(map[string]string{ "BP_WEB_SERVER": "nginx", "SERVICE_BINDING_ROOT": "/bindings", diff --git a/integration/offline_test.go b/integration/offline_test.go index daccea0f..7be12536 100644 --- a/integration/offline_test.go +++ b/integration/offline_test.go @@ -51,7 +51,7 @@ func testOffline(t *testing.T, context spec.G, it spec.S) { var err error image, _, err = pack.WithNoColor().Build. WithPullPolicy("never"). - WithBuildpacks(offlineNginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Offline). WithNetwork("none"). Execute(name, source) Expect(err).NotTo(HaveOccurred()) diff --git a/integration/require_test.go b/integration/require_test.go new file mode 100644 index 00000000..160252ea --- /dev/null +++ b/integration/require_test.go @@ -0,0 +1,88 @@ +package integration_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/paketo-buildpacks/occam" + "github.com/sclevine/spec" + + . "github.com/onsi/gomega" + . "github.com/paketo-buildpacks/occam/matchers" +) + +func testRequire(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + + pack occam.Pack + docker occam.Docker + + name string + source string + sbomDir string + + image occam.Image + container occam.Container + ) + + it.Before(func() { + pack = occam.NewPack().WithNoColor().WithVerbose() + docker = occam.NewDocker() + + var err error + name, err = occam.RandomName() + Expect(err).NotTo(HaveOccurred()) + + source, err = os.MkdirTemp("", "require") + Expect(err).NotTo(HaveOccurred()) + + Expect(os.WriteFile(filepath.Join(source, "plan.toml"), []byte(` +[[requires]] + name = "nginx" + + [requires.metadata] + launch = true +`), 0600)).To(Succeed()) + }) + + it.After(func() { + if container.ID != "" { + Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) + } + + if image.ID != "" { + Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) + } + + Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) + + Expect(os.RemoveAll(source)).To(Succeed()) + Expect(os.RemoveAll(sbomDir)).To(Succeed()) + }) + + it("installs nginx into the container", func() { + var err error + image, _, err = pack.Build. + WithBuildpacks( + settings.Buildpacks.NGINX.Online, + settings.Buildpacks.BuildPlan.Online, + ). + WithPullPolicy("never"). + WithSBOMOutputDir(sbomDir). + Execute(name, source) + Expect(err).NotTo(HaveOccurred()) + + container, err = docker.Container.Run. + WithCommand("nginx -v"). + Execute(image.ID) + Expect(err).ToNot(HaveOccurred()) + + logs, err := docker.Container.Logs.Execute(container.ID) + Expect(err).ToNot(HaveOccurred()) + Expect(logs).To(ContainLines( + MatchRegexp(`nginx version: nginx\/\d+\.\d+\.\d+`), + )) + }) +} diff --git a/integration/simple_app_test.go b/integration/simple_app_test.go index 20c5a370..f4d49f32 100644 --- a/integration/simple_app_test.go +++ b/integration/simple_app_test.go @@ -67,7 +67,7 @@ func testSimpleApp(t *testing.T, context spec.G, it spec.S) { it("serves up staticfile", func() { var err error image, _, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithPullPolicy("never"). WithSBOMOutputDir(sbomDir). Execute(name, source) @@ -86,12 +86,12 @@ func testSimpleApp(t *testing.T, context spec.G, it spec.S) { Expect(string(contents)).To(ContainSubstring(`"name":"Nginx Server"`)) // check that all required SBOM files are present - Expect(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_"), "nginx", "sbom.cdx.json")).To(BeARegularFile()) - Expect(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_"), "nginx", "sbom.spdx.json")).To(BeARegularFile()) - Expect(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_"), "nginx", "sbom.syft.json")).To(BeARegularFile()) + Expect(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(settings.Buildpack.ID, "/", "_"), "nginx", "sbom.cdx.json")).To(BeARegularFile()) + Expect(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(settings.Buildpack.ID, "/", "_"), "nginx", "sbom.spdx.json")).To(BeARegularFile()) + Expect(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(settings.Buildpack.ID, "/", "_"), "nginx", "sbom.syft.json")).To(BeARegularFile()) // check an SBOM file to make sure it has an entry - contents, err = os.ReadFile(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_"), "nginx", "sbom.cdx.json")) + contents, err = os.ReadFile(filepath.Join(sbomDir, "sbom", "launch", strings.ReplaceAll(settings.Buildpack.ID, "/", "_"), "nginx", "sbom.cdx.json")) Expect(err).NotTo(HaveOccurred()) Expect(string(contents)).To(ContainSubstring(`"name": "Nginx Server"`)) }) @@ -107,7 +107,7 @@ func testSimpleApp(t *testing.T, context spec.G, it spec.S) { it("starts successfully", func() { var err error image, _, err = pack.Build. - WithBuildpacks(nginxBuildpack). + WithBuildpacks(settings.Buildpacks.NGINX.Online). WithPullPolicy("never"). Execute(name, source) Expect(err).NotTo(HaveOccurred()) @@ -148,8 +148,8 @@ func testSimpleApp(t *testing.T, context spec.G, it spec.S) { var logs fmt.Stringer image, logs, err = pack.Build. WithBuildpacks( - watchexecBuildpack, - nginxBuildpack, + settings.Buildpacks.Watchexec.Online, + settings.Buildpacks.NGINX.Online, ). WithPullPolicy("never"). WithEnv(map[string]string{ @@ -199,8 +199,8 @@ func testSimpleApp(t *testing.T, context spec.G, it spec.S) { var logs fmt.Stringer _, logs, err = pack.Build. WithBuildpacks( - watchexecBuildpack, - nginxBuildpack, + settings.Buildpacks.Watchexec.Online, + settings.Buildpacks.NGINX.Online, ). WithPullPolicy("never"). WithEnv(map[string]string{ diff --git a/parser.go b/parser.go index e3592efe..ab4e6024 100644 --- a/parser.go +++ b/parser.go @@ -35,7 +35,6 @@ func (p Parser) ParseYml(workingDir string) (string, bool, error) { } func (p Parser) ResolveVersion(cnbPath, version string) (string, error) { - bpTOML, err := os.Open(filepath.Join(cnbPath, "buildpack.toml")) if err != nil { return "", err diff --git a/run/main.go b/run/main.go index 59fe0cf0..2e280943 100644 --- a/run/main.go +++ b/run/main.go @@ -4,7 +4,6 @@ import ( "fmt" "os" - "github.com/caarlos0/env/v6" "github.com/paketo-buildpacks/nginx" "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/cargo" @@ -26,20 +25,18 @@ func (f Generator) GenerateFromDependency(dependency postal.Dependency, path str func main() { logger := scribe.NewEmitter(os.Stdout) - var buildEnv nginx.BuildEnvironment - err := env.Parse(&buildEnv) + config, err := nginx.LoadConfiguration(os.Environ(), servicebindings.NewResolver(), os.Getenv("CNB_PLATFORM_DIR")) if err != nil { fmt.Fprintln(os.Stderr, fmt.Errorf("failed to parse build configuration: %w", err)) os.Exit(1) } packit.Run( - nginx.Detect(buildEnv, nginx.NewParser()), + nginx.Detect(config, nginx.NewParser()), nginx.Build( - buildEnv, + config, draft.NewPlanner(), postal.NewService(cargo.NewTransport()), - servicebindings.NewResolver(), nginx.NewDefaultConfigGenerator(logger), fs.NewChecksumCalculator(), Generator{},