Skip to content

Commit e411718

Browse files
authored
Enable postgis extensions automatically (CrunchyData#2760)
* Enable postgis extensions automatically on postgis images Issue: [sc-12487]
1 parent e3ea540 commit e411718

File tree

4 files changed

+123
-2
lines changed

4 files changed

+123
-2
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
- uses: nolar/setup-k3d-k3s@v1
4848
with:
4949
version: "${{ matrix.kubernetes }}"
50+
k3d-tag: v4.4.8
5051
k3d-args: --no-lb
5152
- name: Prefetch container images
5253
run: >

internal/controller/postgrescluster/postgres.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/crunchydata/postgres-operator/internal/logging"
3838
"github.com/crunchydata/postgres-operator/internal/naming"
3939
"github.com/crunchydata/postgres-operator/internal/pgaudit"
40+
"github.com/crunchydata/postgres-operator/internal/postgis"
4041
"github.com/crunchydata/postgres-operator/internal/postgres"
4142
pgpassword "github.com/crunchydata/postgres-operator/internal/postgres/password"
4243
"github.com/crunchydata/postgres-operator/internal/util"
@@ -188,7 +189,7 @@ func (r *Reconciler) reconcilePostgresDatabases(
188189

189190
// Calculate a hash of the SQL that should be executed in PostgreSQL.
190191

191-
var pgAuditOK bool
192+
var pgAuditOK, postgisInstallOK bool
192193
create := func(ctx context.Context, exec postgres.Executor) error {
193194
if pgAuditOK = pgaudit.EnableInPostgreSQL(ctx, exec) == nil; !pgAuditOK {
194195
// pgAudit can only be enabled after its shared library is loaded,
@@ -201,6 +202,18 @@ func (r *Reconciler) reconcilePostgresDatabases(
201202
"Unable to install pgAudit; try restarting PostgreSQL")
202203
}
203204

205+
// Enabling PostGIS extensions is a one-way operation
206+
// e.g., you can take a PostgresCluster and turn it into a PostGISCluster,
207+
// but you cannot reverse the process, as that would potentially remove an extension
208+
// that is being used by some database/tables
209+
if cluster.Spec.PostGISVersion != "" {
210+
if postgisInstallOK = postgis.EnableInPostgreSQL(ctx, exec) == nil; !postgisInstallOK {
211+
// TODO(benjb): Investigate under what conditions postgis would fail install
212+
r.Recorder.Event(cluster, corev1.EventTypeWarning, "PostGISDisabled",
213+
"Unable to install PostGIS")
214+
}
215+
}
216+
204217
return postgres.CreateDatabasesInPostgreSQL(ctx, exec, databases.List())
205218
}
206219

@@ -232,7 +245,7 @@ func (r *Reconciler) reconcilePostgresDatabases(
232245
log := logging.FromContext(ctx).WithValues("revision", revision)
233246
err = errors.WithStack(create(logging.NewContext(ctx, log), podExecutor))
234247
}
235-
if err == nil && pgAuditOK {
248+
if err == nil && pgAuditOK && postgisInstallOK {
236249
cluster.Status.DatabaseRevision = revision
237250
}
238251

internal/postgis/postgis.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2021 Crunchy Data Solutions, Inc.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package postgis
17+
18+
import (
19+
"context"
20+
"strings"
21+
22+
"github.com/crunchydata/postgres-operator/internal/logging"
23+
"github.com/crunchydata/postgres-operator/internal/postgres"
24+
)
25+
26+
// EnableInPostgreSQL installs triggers for the following extensions into every database:
27+
// - postgis
28+
// - postgis_topology
29+
// - fuzzystrmatch
30+
// - postgis_tiger_geocoder
31+
func EnableInPostgreSQL(ctx context.Context, exec postgres.Executor) error {
32+
log := logging.FromContext(ctx)
33+
34+
stdout, stderr, err := exec.ExecInAllDatabases(ctx,
35+
strings.Join([]string{
36+
// Quiet NOTICE messages from IF NOT EXISTS statements.
37+
// - https://www.postgresql.org/docs/current/runtime-config-client.html
38+
`SET client_min_messages = WARNING;`,
39+
40+
`CREATE EXTENSION IF NOT EXISTS postgis;`,
41+
`CREATE EXTENSION IF NOT EXISTS postgis_topology;`,
42+
`CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;`,
43+
`CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;`,
44+
}, "\n"),
45+
map[string]string{
46+
"ON_ERROR_STOP": "on", // Abort when any one statement fails.
47+
"QUIET": "on", // Do not print successful statements to stdout.
48+
})
49+
50+
log.V(1).Info("enabled PostGIS and related extensions", "stdout", stdout, "stderr", stderr)
51+
52+
return err
53+
}

internal/postgis/postgis_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2021 Crunchy Data Solutions, Inc.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package postgis
17+
18+
import (
19+
"context"
20+
"errors"
21+
"io"
22+
"io/ioutil"
23+
"strings"
24+
"testing"
25+
26+
"gotest.tools/v3/assert"
27+
)
28+
29+
func TestEnableInPostgreSQL(t *testing.T) {
30+
expected := errors.New("whoops")
31+
exec := func(
32+
_ context.Context, stdin io.Reader, stdout, stderr io.Writer, command ...string,
33+
) error {
34+
assert.Assert(t, stdout != nil, "should capture stdout")
35+
assert.Assert(t, stderr != nil, "should capture stderr")
36+
37+
assert.Assert(t, strings.Contains(strings.Join(command, "\n"),
38+
`SELECT datname FROM pg_catalog.pg_database`,
39+
), "expected all databases and templates")
40+
41+
b, err := ioutil.ReadAll(stdin)
42+
assert.NilError(t, err)
43+
assert.Equal(t, string(b), `SET client_min_messages = WARNING;
44+
CREATE EXTENSION IF NOT EXISTS postgis;
45+
CREATE EXTENSION IF NOT EXISTS postgis_topology;
46+
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
47+
CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;`)
48+
49+
return expected
50+
}
51+
52+
ctx := context.Background()
53+
assert.Equal(t, expected, EnableInPostgreSQL(ctx, exec))
54+
}

0 commit comments

Comments
 (0)