Skip to content

Commit

Permalink
feat: postgres client tools (#22)
Browse files Browse the repository at this point in the history
* docs: add psql to main readme

* ui: apt folder creation logs garbled

* feat: add extra postgresql client binaries

* fix: bump buildpack versions for ui fix on apt install
  • Loading branch information
acodeninja authored Nov 4, 2024
1 parent ad8cb75 commit 3ad1f6e
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 79 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

ACodeNinja's Collection of cloud native buildpacks. All buildpacks are released under the [MIT license](./LICENSE).

- [Playwright](./playwright) - Adds requirements for running [playwright](https://playwright.dev/python/)
- [Python Security](./python-security) - Adds requirements for using Python Security packages.
- [Install](./install) - Install packages from the Ubuntu apt repositories.
- [`acodeninja/install`](./install) - Install packages from the Ubuntu apt repositories.
- [`acodeninja/playwright`](./playwright) - Adds requirements for running [playwright](https://playwright.dev/python/)
- [`acodeninja/python-security`](./python-security) - Adds requirements for using Python Security packages.
- [`acodeninja/psql`](./psql) - Adds several PostgreSQL client tools.
2 changes: 1 addition & 1 deletion common/apt/apt.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func InstallAptPackages(layer libcnb.Layer, packageList []string, logger bard.Lo
if err != nil {
return err
}
logger.Body(" Created", directory)
logger.Body(" Created ", directory)
}

err = common.CopyFile("/etc/apt/sources.list", fmt.Sprintf("%s/sources.list", aptSourcesDirectory))
Expand Down
2 changes: 1 addition & 1 deletion install/buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ api = "0.8"
[buildpack]
id = "acodeninja/install"
name = "ACodeNinja Buildpack for installing system packages"
version = "1.0.0"
version = "1.0.1"
homepage = "https://github.com/acodeninja/buildpacks/tree/main/install"

[[buildpack.licenses]]
Expand Down
2 changes: 1 addition & 1 deletion playwright/buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ api = "0.8"
[buildpack]
id = "acodeninja/playwright"
name = "ACodeNinja Buildpack for Playwright"
version = "3.0.1"
version = "3.0.2"
homepage = "https://github.com/acodeninja/buildpacks/tree/main/playwright"

[[buildpack.licenses]]
Expand Down
12 changes: 12 additions & 0 deletions psql/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# ACodeNinja Buildpack for PostgreSQL client

**Buildpack id**: `acodeninja/psql`

This buildpack installs the PostgreSQL client and associated tools.

- `pg_amcheck`
- `pgbench`
- `pg_config`
- `pg_dump`
- `pg_dumpall`
- `pg_isready`
- `pg_receivewal`
- `pg_restore`
- `psql`
2 changes: 1 addition & 1 deletion psql/buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ api = "0.8"
[buildpack]
id = "acodeninja/psql"
name = "ACodeNinja Buildpack for PostgreSQL client"
version = "1.0.0"
version = "1.1.0"
homepage = "https://github.com/acodeninja/buildpacks/tree/main/psql"

[[buildpack.licenses]]
Expand Down
52 changes: 33 additions & 19 deletions psql/fixtures/bash/test.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
#!/usr/bin/env bash

PSQL_WHICH_TEST_OUTCOME=""
PSQL_WHICH="$(which psql)"
if [[ "$PSQL_WHICH" == "/layers/acodeninja_psql/psql/psql-bin/psql" ]]
then
PSQL_WHICH_TEST_OUTCOME="passed 🟢"
else
PSQL_WHICH_TEST_OUTCOME="psql is located at $PSQL_WHICH 🔴"
fi
echo "psql command location: $PSQL_WHICH_TEST_OUTCOME"
TESTS_FAILED=""
CURRENT_TEST_GROUP_NAME=""

PSQL_VERSION_TEST_OUTCOME=""
PSQL_VERSION="$(psql --version)"
if [[ "$PSQL_VERSION" =~ "(PostgreSQL) 14" ]]
then
PSQL_VERSION_TEST_OUTCOME="passed 🟢"
else
PSQL_VERSION_TEST_OUTCOME="psql version is $PSQL_VERSION 🔴"
fi
echo "psql command version: $PSQL_VERSION_TEST_OUTCOME"
function test_group() {
TEST_GROUP_NAME=$1
CURRENT_TEST_GROUP_NAME="$TEST_GROUP_NAME"
}

function run_test() {
TEST_NAME=$1
TEST_COMMAND=$2
TEST_EXPECTED_OUTPUT=$3

TEST_OUTCOME=""
TEST_OUTPUT=`$TEST_COMMAND 2>&1`
if [[ "$TEST_OUTPUT" =~ "$TEST_EXPECTED_OUTPUT" ]]
then
TEST_OUTCOME="passed 🟢"
else
TEST_OUTCOME="failed 🔴 - $TEST_OUTPUT"
TESTS_FAILED="yes"
fi
echo "[$CURRENT_TEST_GROUP_NAME][$TEST_NAME]: $TEST_OUTCOME"
}

COMMANDS_TO_TEST="pg_amcheck pgbench pg_config pg_dump pg_dumpall pg_isready pg_receivewal pg_restore psql"

for COMMAND_TO_TEST in $COMMANDS_TO_TEST; do
test_group "$COMMAND_TO_TEST"
run_test "location" "which $COMMAND_TO_TEST" "/layers/acodeninja_psql/psql/psql-bin/$COMMAND_TO_TEST"
run_test "$COMMAND_TO_TEST version" "$COMMAND_TO_TEST --version" "14"
run_test "$COMMAND_TO_TEST server" "$COMMAND_TO_TEST --version" "PostgreSQL"
done

if [[ "$PSQL_VERSION_TEST_OUTCOME" == "passed 🟢" && "$PSQL_VERSION_TEST_OUTCOME" == "passed 🟢" ]]
if [[ -z "$TESTS_FAILED" ]]
then
exit 0
else
Expand Down
129 changes: 78 additions & 51 deletions psql/psql.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import (
"text/template"
)

//go:embed psql.sh
//go:embed wrapper.sh
var embeddedFiles embed.FS

type PSQLScriptData struct {
type WrapperScriptInput struct {
LibLocations []string
PerlLibLocation string
PostgresClientPath string
PostgresCommand string
}

type PostgresClientLayer struct {
Expand Down Expand Up @@ -77,59 +78,28 @@ func (psql PostgresClientLayer) Contribute(layer libcnb.Layer) (libcnb.Layer, er
return libcnb.Layer{}, fmt.Errorf("unable to install postgresql-client\n%w", err)
}

psql.Logger.Header("Installing bash wrapper")

psqlScript, err := embeddedFiles.ReadFile("psql.sh")
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to read embeded psql script\n%w", err)
}

psqlFileTemplate, err := template.New("psql").Parse(string(psqlScript))
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to parse psql script template\n%w", err)
}

psqlFileLocation := fmt.Sprintf("%s/psql-bin", layer.Path)

err = os.MkdirAll(psqlFileLocation, os.ModePerm)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to create psql script location\n%w", err)
}

psqlFile, err := os.Create(fmt.Sprintf("%s/psql", psqlFileLocation))
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to create psql wrapper file\n%w", err)
}

err = psqlFileTemplate.Execute(psqlFile, PSQLScriptData{
LibLocations: []string{
fmt.Sprintf("%s/usr/lib/x86_64-linux-gnu/sasl2", layer.Path),
fmt.Sprintf("%s/usr/lib/x86_64-linux-gnu", layer.Path),
fmt.Sprintf("%s/lib/x86_64-linux-gnu", layer.Path),
},
PerlLibLocation: fmt.Sprintf("%s/usr/share/perl5", layer.Path),
PostgresClientPath: fmt.Sprintf("%s/usr/lib/postgresql/14/bin", layer.Path),
})
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to create psql wrapper file\n%w", err)
psql.Logger.Header("Installing command wrappers")
commandsToWrap := []string{
"pg_amcheck",
"pgbench",
"pg_config",
"pg_dump",
"pg_dumpall",
"pg_isready",
"pg_receivewal",
"pg_restore",
"psql",
}

err = psqlFile.Sync()
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to sync psql script\n%w", err)
for _, command := range commandsToWrap {
err = WriteWrapperToBin(layer, psql.Logger, command)
if err != nil {
return libcnb.Layer{}, err
}
}

err = psqlFile.Close()
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to close psql script\n%w", err)
}

err = os.Chmod(fmt.Sprintf("%s/psql", psqlFileLocation), 0775)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to chmod psql script\n%w", err)
}

layer.SharedEnvironment.Prepend("PATH", ":", psqlFileLocation)
psql.Logger.Header("Writing environment")
layer.SharedEnvironment.Prepend("PATH", ":", fmt.Sprintf("%s/psql-bin", layer.Path))

layer.LayerTypes.Build = true
layer.LayerTypes.Launch = true
Expand All @@ -143,6 +113,63 @@ func (psql PostgresClientLayer) Name() string {
return "psql"
}

func WriteWrapperToBin(layer libcnb.Layer, logger bard.Logger, pgBinary string) error {
logger.Bodyf("Writing wrapper script for %s", pgBinary)

script, err := embeddedFiles.ReadFile("wrapper.sh")
if err != nil {
return fmt.Errorf("unable to read embeded %s script\n%w", pgBinary, err)
}

wrapperFileTemplate, err := template.New("wrapper").Parse(string(script))
if err != nil {
return fmt.Errorf("unable to parse %s script template\n%w", pgBinary, err)
}

wrapperFileLocation := fmt.Sprintf("%s/psql-bin", layer.Path)

err = os.MkdirAll(wrapperFileLocation, os.ModePerm)
if err != nil {
return fmt.Errorf("unable to create %s script location\n%w", pgBinary, err)
}

wrapperFile, err := os.Create(fmt.Sprintf("%s/%s", wrapperFileLocation, pgBinary))
if err != nil {
return fmt.Errorf("unable to create %s wrapper file\n%w", pgBinary, err)
}

err = wrapperFileTemplate.Execute(wrapperFile, WrapperScriptInput{
LibLocations: []string{
fmt.Sprintf("%s/usr/lib/x86_64-linux-gnu/sasl2", layer.Path),
fmt.Sprintf("%s/usr/lib/x86_64-linux-gnu", layer.Path),
fmt.Sprintf("%s/lib/x86_64-linux-gnu", layer.Path),
},
PerlLibLocation: fmt.Sprintf("%s/usr/share/perl5", layer.Path),
PostgresClientPath: fmt.Sprintf("%s/usr/lib/postgresql/14/bin", layer.Path),
PostgresCommand: pgBinary,
})
if err != nil {
return fmt.Errorf("unable to create %s wrapper file\n%w", pgBinary, err)
}

err = wrapperFile.Sync()
if err != nil {
return fmt.Errorf("unable to sync %s script\n%w", pgBinary, err)
}

err = wrapperFile.Close()
if err != nil {
return fmt.Errorf("unable to close %s script\n%w", pgBinary, err)
}

err = os.Chmod(fmt.Sprintf("%s/%s", wrapperFileLocation, pgBinary), 0775)
if err != nil {
return fmt.Errorf("unable to chmod %s script\n%w", pgBinary, err)
}

return nil
}

func ResolvePostgresClientVersion(logger bard.Logger) string {
psqlVersion := "14"
ubuntuVersion := ResolveUbuntuVersion(logger)
Expand Down
2 changes: 1 addition & 1 deletion psql/psql.sh → psql/wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ PATH="{{.PostgresClientPath}}:$PATH"
LD_LIBRARY_PATH="{{range $val := .LibLocations}}{{$val}}:{{end}}$LD_LIBRARY_PATH"
LIBRARY_PATH="{{range $val := .LibLocations}}{{$val}}:{{end}}$LD_LIBRARY_PATH"

psql $@
{{.PostgresCommand}} $@
2 changes: 1 addition & 1 deletion python-security/buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ api = "0.8"
[buildpack]
id = "acodeninja/python-security"
name = "ACodeNinja Buildpack for Python Security"
version = "2.0.2"
version = "2.0.3"
homepage = "https://github.com/acodeninja/buildpacks/tree/main/python-security"

[[buildpack.licenses]]
Expand Down

0 comments on commit 3ad1f6e

Please sign in to comment.