Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use exec.d to set PYTHONPYCACHEPREFIX #352

Merged
merged 1 commit into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func Build(dependencies DependencyManager, sbomGenerator SBOMGenerator, logger s
}

cpythonLayer.SharedEnv.Default("PYTHONPATH", cpythonLayer.Path)
cpythonLayer.SharedEnv.Default("PYTHONPYCACHEPREFIX", "$HOME/.pycache")
cpythonLayer.ExecD = []string{filepath.Join(context.CNBPath, "bin", "env")}

logger.Break()

Expand Down
7 changes: 5 additions & 2 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(layer.Path).To(Equal(filepath.Join(layersDir, "cpython")))

Expect(layer.SharedEnv).To(Equal(packit.Environment{
"PYTHONPATH.default": filepath.Join(layersDir, "cpython"),
"PYTHONPYCACHEPREFIX.default": "$HOME/.pycache",
"PYTHONPATH.default": filepath.Join(layersDir, "cpython"),
}))
Expect(layer.BuildEnv).To(BeEmpty())
Expect(layer.LaunchEnv).To(BeEmpty())
Expand All @@ -132,6 +131,10 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
"dependency-sha": "python-dependency-sha",
}))

Expect(layer.ExecD).To(Equal([]string{
filepath.Join(cnbDir, "bin", "env"),
}))

Expect(layer.SBOM.Formats()).To(Equal([]packit.SBOMFormat{
{
Extension: sbom.Format(sbom.CycloneDXFormat).Extension(),
Expand Down
2 changes: 1 addition & 1 deletion buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ api = "0.7"
uri = "https://github.com/paketo-buildpacks/cpython/blob/main/LICENSE"

[metadata]
include-files = ["bin/run", "bin/build", "bin/detect", "buildpack.toml"]
include-files = ["bin/run", "bin/build", "bin/detect", "bin/env", "buildpack.toml"]
pre-package = "./scripts/build.sh"
[metadata.default-versions]
python = "3.9.x"
Expand Down
14 changes: 14 additions & 0 deletions cmd/env/internal/init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package internal_test

import (
"testing"

"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
)

func TestUnitEnv(t *testing.T) {
suite := spec.New("cmd/env/internal", spec.Report(report.Terminal{}))
suite("Run", testRun)
suite.Run(t)
}
36 changes: 36 additions & 0 deletions cmd/env/internal/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package internal

import (
"io"
"path/filepath"
"strings"

"github.com/BurntSushi/toml"
)

// Run will write a TOML environment definition to the output io.Writer with
// the following rules:
// 1. If $PYTHONPYCACHEPREFIX is set, do nothing
// 2. If $PYTHONPYCACHEPREFIX is unset, set it to the expanded value of
// $HOME/.pycache
func Run(env []string, output io.Writer) error {
robdimsdale marked this conversation as resolved.
Show resolved Hide resolved
var home string
for _, v := range env {
if strings.HasPrefix(v, "HOME=") {
home = strings.TrimPrefix(v, "HOME=")
}

if strings.HasPrefix(v, "PYTHONPYCACHEPREFIX=") {
return nil
}
}

err := toml.NewEncoder(output).Encode(map[string]string{
"PYTHONPYCACHEPREFIX": filepath.Join(home, ".pycache"),
})
if err != nil {
return err
}

return nil
}
61 changes: 61 additions & 0 deletions cmd/env/internal/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package internal_test

import (
"bytes"
"os"
"testing"

"github.com/paketo-buildpacks/cpython/cmd/env/internal"
"github.com/sclevine/spec"

. "github.com/onsi/gomega"
. "github.com/paketo-buildpacks/packit/v2/matchers"
)

func testRun(t *testing.T, context spec.G, it spec.S) {
var Expect = NewWithT(t).Expect

context("when $PYTHONPYCACHEPREFIX is not set", func() {
it("sets it to $HOME/.pycache", func() {
buffer := bytes.NewBuffer(nil)
err := internal.Run([]string{"HOME=/some-home"}, buffer)
Expect(err).NotTo(HaveOccurred())

Expect(buffer.String()).To(MatchTOML(`
PYTHONPYCACHEPREFIX = "/some-home/.pycache"
`))
})
})

context("when $PYTHONPYCACHEPREFIX is already set", func() {
it("preserves the existing value", func() {
buffer := bytes.NewBuffer(nil)
err := internal.Run([]string{
"HOME=/some-home",
"PYTHONPYCACHEPREFIX=/other-home/.pycache",
}, buffer)
Expect(err).NotTo(HaveOccurred())

Expect(buffer.String()).To(BeEmpty())
})
})

context("failure cases", func() {
context("when the output cannot be written to", func() {
var file *os.File

it.Before(func() {
file, err := os.CreateTemp("", "")
Expect(err).NotTo(HaveOccurred())

Expect(file.Close()).To(Succeed())
Expect(os.Remove(file.Name())).To(Succeed())
})

it("returns an error", func() {
err := internal.Run([]string{"HOME=/some-home"}, file)
Expect(err).To(MatchError("invalid argument"))
})
})
})
}
16 changes: 16 additions & 0 deletions cmd/env/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"log"
"os"

"github.com/paketo-buildpacks/cpython/cmd/env/internal"
)

// env will set environment variables that are dynamically defined at runtime
func main() {
err := internal.Run(os.Environ(), os.NewFile(3, "/dev/fd/3"))
robdimsdale marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Fatal(err)
}
}
17 changes: 8 additions & 9 deletions integration/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {
Expect(err).ToNot(HaveOccurred())

Eventually(container).Should(BeAvailable())
Eventually(container).Should(Serve(ContainSubstring("hello world")).OnPort(8080))
Eventually(container).Should(Serve(SatisfyAll(
ContainSubstring("hello world"),
ContainSubstring("PYTHONPYCACHEPREFIX=/home/cnb/.pycache"),
)).OnPort(8080))

Expect(logs).To(ContainLines(
MatchRegexp(fmt.Sprintf(`%s \d+\.\d+\.\d+`, buildpackInfo.Buildpack.Name)),
Expand All @@ -84,12 +87,10 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {
))
Expect(logs).To(ContainLines(
" Configuring build environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
"",
" Configuring launch environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
))
})

Expand Down Expand Up @@ -218,12 +219,10 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {

Expect(logs).To(ContainLines(
" Configuring build environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
"",
" Configuring launch environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
))
})
})
Expand Down
3 changes: 1 addition & 2 deletions integration/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func TestIntegration(t *testing.T) {
// Do not truncate Gomega matcher output
// The buildpack output text can be large and we often want to see all of it.
format.MaxLength = 0
SetDefaultEventuallyTimeout(30 * time.Second)

Expect := NewWithT(t).Expect

Expand Down Expand Up @@ -83,8 +84,6 @@ func TestIntegration(t *testing.T) {
Execute(settings.Config.BuildPlan)
Expect(err).NotTo(HaveOccurred())

SetDefaultEventuallyTimeout(5 * time.Second)

suite := spec.New("Integration", spec.Report(report.Terminal{}), spec.Parallel())
suite("Default", testDefault)
suite("Offline", testOffline)
Expand Down
21 changes: 12 additions & 9 deletions integration/testdata/default_app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@

class Server(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()
self.wfile.write(bytes("hello world", "utf8"))
return
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()

self.wfile.write(bytes("hello world", "utf8"))

prefix = os.getenv("PYTHONPYCACHEPREFIX")
self.wfile.write(bytes(f'PYTHONPYCACHEPREFIX={prefix}', "utf8"))

def do_HEAD(self):
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()
return
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()

port = int(os.getenv("PORT", "8080"))
server_address = ("", port)
Expand Down