Skip to content

Commit 1adec86

Browse files
authored
Wait for (DB) cache within the code instead of using initcontainer (kptdev#473)
* wait for cache within the code instead of using init container, re-sort go.mod * min change to retrigger CI
1 parent 28492d4 commit 1adec86

5 files changed

Lines changed: 58 additions & 68 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ __debug*
4545
docs/.hugo_build.lock
4646
docs/public/
4747
docs/resources/
48+
docs/go.sum
4849

4950
# Temporary lock file while building
5051
/.hugo_build.lock
5152

5253
# gosec artifacts
53-
*.sarif
54+
*.sarif

deployments/porch/3-porch-server.yaml

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,6 @@ spec:
3333
app: porch-server
3434
spec:
3535
serviceAccountName: porch-server
36-
initContainers:
37-
- name: wait-for-postgres
38-
image: postgres:17-alpine
39-
command:
40-
- sh
41-
- -c
42-
- |
43-
until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do
44-
echo "Waiting for PostgreSQL to be ready..."
45-
sleep 2
46-
done
47-
echo "PostgreSQL is ready!"
48-
envFrom:
49-
- configMapRef:
50-
name: porch-db-config
51-
- secretRef:
52-
name: porch-db-secret
5336
volumes:
5437
- name: cache-volume
5538
emptyDir: {}

go.mod

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@ require (
2323
github.com/kptdev/krm-functions-sdk/go/fn v1.0.2
2424
github.com/pkg/errors v0.9.1
2525
github.com/prometheus/client_golang v1.23.2
26+
github.com/robfig/cron/v3 v3.0.1
2627
github.com/spf13/cobra v1.10.2
2728
github.com/spf13/pflag v1.0.10
2829
github.com/stretchr/testify v1.11.1
2930
go.opentelemetry.io/contrib/bridges/prometheus v0.63.0
3031
go.opentelemetry.io/contrib/exporters/autoexport v0.63.0
32+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0
3133
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
3234
go.opentelemetry.io/contrib/propagators/autoprop v0.63.0
3335
go.opentelemetry.io/otel v1.38.0
3436
go.opentelemetry.io/otel/sdk v1.38.0
3537
go.opentelemetry.io/otel/sdk/metric v1.38.0
3638
go.opentelemetry.io/otel/trace v1.38.0
39+
go.opentelemetry.io/proto/otlp v1.9.0
3740
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546
3841
golang.org/x/oauth2 v0.32.0
3942
google.golang.org/api v0.254.0
@@ -59,47 +62,6 @@ require (
5962
sigs.k8s.io/yaml v1.6.0
6063
)
6164

62-
require github.com/go-errors/errors v1.5.1 // indirect
63-
64-
require (
65-
github.com/Masterminds/semver/v3 v3.4.0 // indirect
66-
github.com/bytecodealliance/wasmtime-go v1.0.0 // indirect
67-
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
68-
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
69-
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
70-
github.com/kptdev/krm-functions-catalog/functions/go/apply-setters v0.2.2 // indirect
71-
github.com/onsi/ginkgo/v2 v2.23.3 // indirect
72-
github.com/otiai10/copy v1.14.1 // indirect
73-
github.com/prep/wasmexec v0.0.0-20220807105708-6554945c1dec // indirect
74-
github.com/qri-io/starlib v0.5.0 // indirect
75-
github.com/regclient/regclient v0.11.1 // indirect
76-
github.com/ulikunitz/xz v0.5.15 // indirect
77-
go.starlark.net v0.0.0-20251029211736-7849196f18cf // indirect
78-
golang.org/x/mod v0.29.0 // indirect
79-
sigs.k8s.io/kustomize/api v0.20.1 // indirect
80-
)
81-
82-
require (
83-
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
84-
github.com/prometheus/otlptranslator v0.0.2 // indirect
85-
go.opentelemetry.io/contrib/propagators/aws v1.38.0 // indirect
86-
go.opentelemetry.io/contrib/propagators/b3 v1.38.0 // indirect
87-
go.opentelemetry.io/contrib/propagators/jaeger v1.38.0 // indirect
88-
go.opentelemetry.io/contrib/propagators/ot v1.38.0 // indirect
89-
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 // indirect
90-
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect
91-
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
92-
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect
93-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
94-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
95-
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 // indirect
96-
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0 // indirect
97-
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 // indirect
98-
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect
99-
go.opentelemetry.io/otel/log v0.14.0 // indirect
100-
go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect
101-
)
102-
10365
require (
10466
cel.dev/expr v0.25.0 // indirect
10567
cloud.google.com/go/auth v0.17.0 // indirect
@@ -109,6 +71,7 @@ require (
10971
github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect
11072
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
11173
github.com/MakeNowJust/heredoc v1.0.0 // indirect
74+
github.com/Masterminds/semver/v3 v3.4.0 // indirect
11275
github.com/Microsoft/go-winio v0.6.2 // indirect
11376
github.com/NYTimes/gziphandler v1.1.1 // indirect
11477
github.com/ProtonMail/go-crypto v1.3.0 // indirect
@@ -117,25 +80,29 @@ require (
11780
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
11881
github.com/beorn7/perks v1.0.1 // indirect
11982
github.com/blang/semver/v4 v4.0.0 // indirect
83+
github.com/bytecodealliance/wasmtime-go v1.0.0 // indirect
12084
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
12185
github.com/cespare/xxhash/v2 v2.3.0 // indirect
12286
github.com/chai2010/gettext-go v1.0.3 // indirect
12387
github.com/cloudflare/circl v1.6.1 // indirect
12488
github.com/containerd/stargz-snapshotter/estargz v0.18.0 // indirect
12589
github.com/coreos/go-semver v0.3.1 // indirect
12690
github.com/coreos/go-systemd/v22 v22.6.0 // indirect
91+
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
12792
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
12893
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
12994
github.com/docker/cli v28.5.1+incompatible // indirect
13095
github.com/docker/distribution v2.8.3+incompatible // indirect
13196
github.com/docker/docker-credential-helpers v0.9.4 // indirect
97+
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
13298
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e // indirect
13399
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
134100
github.com/emirpasic/gods v1.18.1 // indirect
135101
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
136102
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
137103
github.com/felixge/httpsnoop v1.0.4 // indirect
138104
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
105+
github.com/go-errors/errors v1.5.1 // indirect
139106
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
140107
github.com/go-logr/logr v1.4.3 // indirect
141108
github.com/go-logr/stdr v1.2.2 // indirect
@@ -159,9 +126,11 @@ require (
159126
github.com/google/btree v1.1.3 // indirect
160127
github.com/google/gnostic-models v0.7.0 // indirect
161128
github.com/google/s2a-go v0.1.9 // indirect
129+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
162130
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
163131
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
164132
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
133+
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
165134
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
166135
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
167136
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
@@ -174,6 +143,7 @@ require (
174143
github.com/kevinburke/ssh_config v1.4.0 // indirect
175144
github.com/klauspost/compress v1.18.2 // indirect
176145
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
146+
github.com/kptdev/krm-functions-catalog/functions/go/apply-setters v0.2.2 // indirect
177147
github.com/kylelemons/godebug v1.1.0 // indirect
178148
github.com/lib/pq v1.10.9 // indirect
179149
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
@@ -189,21 +159,26 @@ require (
189159
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
190160
github.com/opencontainers/go-digest v1.0.0 // indirect
191161
github.com/opencontainers/image-spec v1.1.1 // indirect
162+
github.com/otiai10/copy v1.14.1 // indirect
192163
github.com/otiai10/mint v1.6.3 // indirect
193164
github.com/paulmach/orb v0.12.0 // indirect
194165
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
195166
github.com/pjbgf/sha1cd v0.5.0 // indirect
196167
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
168+
github.com/prep/wasmexec v0.0.0-20220807105708-6554945c1dec // indirect
197169
github.com/prometheus/client_model v0.6.2 // indirect
198170
github.com/prometheus/common v0.67.2 // indirect
171+
github.com/prometheus/otlptranslator v0.0.2 // indirect
199172
github.com/prometheus/procfs v0.19.2 // indirect
200-
github.com/robfig/cron/v3 v3.0.1
173+
github.com/qri-io/starlib v0.5.0 // indirect
174+
github.com/regclient/regclient v0.11.1 // indirect
201175
github.com/russross/blackfriday/v2 v2.1.0 // indirect
202176
github.com/sergi/go-diff v1.4.0 // indirect
203177
github.com/sirupsen/logrus v1.9.3 // indirect
204178
github.com/skeema/knownhosts v1.3.2 // indirect
205179
github.com/stoewer/go-strcase v1.3.1 // indirect
206180
github.com/stretchr/objx v0.5.3 // indirect
181+
github.com/ulikunitz/xz v0.5.15 // indirect
207182
github.com/vbatts/tar-split v0.12.2 // indirect
208183
github.com/x448/float16 v0.8.4 // indirect
209184
github.com/xanzy/ssh-agent v0.3.3 // indirect
@@ -214,15 +189,31 @@ require (
214189
go.etcd.io/etcd/client/v3 v3.6.5 // indirect
215190
go.mongodb.org/mongo-driver v1.17.6 // indirect
216191
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
217-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0
192+
go.opentelemetry.io/contrib/propagators/aws v1.38.0 // indirect
193+
go.opentelemetry.io/contrib/propagators/b3 v1.38.0 // indirect
194+
go.opentelemetry.io/contrib/propagators/jaeger v1.38.0 // indirect
195+
go.opentelemetry.io/contrib/propagators/ot v1.38.0 // indirect
196+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 // indirect
197+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect
198+
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
199+
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect
218200
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
201+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
202+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
203+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 // indirect
204+
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0 // indirect
205+
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 // indirect
206+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect
207+
go.opentelemetry.io/otel/log v0.14.0 // indirect
219208
go.opentelemetry.io/otel/metric v1.38.0 // indirect
220-
go.opentelemetry.io/proto/otlp v1.9.0
209+
go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect
210+
go.starlark.net v0.0.0-20251029211736-7849196f18cf // indirect
221211
go.uber.org/multierr v1.11.0 // indirect
222212
go.uber.org/zap v1.27.0 // indirect
223213
go.yaml.in/yaml/v2 v2.4.3 // indirect
224214
go.yaml.in/yaml/v3 v3.0.4 // indirect
225215
golang.org/x/crypto v0.45.0 // indirect
216+
golang.org/x/mod v0.29.0 // indirect
226217
golang.org/x/net v0.47.0 // indirect
227218
golang.org/x/sync v0.18.0 // indirect
228219
golang.org/x/sys v0.38.0 // indirect
@@ -241,6 +232,7 @@ require (
241232
k8s.io/kms v0.34.1 // indirect
242233
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect
243234
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
235+
sigs.k8s.io/kustomize/api v0.20.1 // indirect
244236
sigs.k8s.io/randfill v1.0.0 // indirect
245237
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
246238
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2
210210
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
211211
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
212212
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
213-
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
214-
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
213+
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
214+
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
215215
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
216216
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
217217
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@@ -313,8 +313,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
313313
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
314314
github.com/olareg/olareg v0.1.2 h1:75G8X6E9FUlzL/CSjgFcYfMgNzlc7CxULpUUNsZBIvI=
315315
github.com/olareg/olareg v0.1.2/go.mod h1:TWs+N6pO1S4bdB6eerzUm/ITRQ6kw91mVf9ZYeGtw+Y=
316-
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
317-
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
316+
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
317+
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
318318
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
319319
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
320320
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=

pkg/apiserver/apiserver.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,20 @@ import (
3030
cachetypes "github.com/nephio-project/porch/pkg/cache/types"
3131
"github.com/nephio-project/porch/pkg/engine"
3232
"github.com/nephio-project/porch/pkg/registry/porch"
33+
pkgerrors "github.com/pkg/errors"
3334
"google.golang.org/api/option"
3435
"google.golang.org/api/sts/v1"
3536
corev1 "k8s.io/api/core/v1"
3637
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3738
"k8s.io/apimachinery/pkg/runtime"
3839
"k8s.io/apimachinery/pkg/runtime/schema"
3940
"k8s.io/apimachinery/pkg/runtime/serializer"
41+
"k8s.io/apimachinery/pkg/util/wait"
4042
genericapiserver "k8s.io/apiserver/pkg/server"
4143
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
4244
"k8s.io/client-go/rest"
4345
"k8s.io/client-go/tools/clientcmd"
46+
"k8s.io/client-go/util/retry"
4447
"k8s.io/component-base/compatibility"
4548
"k8s.io/klog/v2"
4649
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -224,10 +227,21 @@ func (c completedConfig) New(ctx context.Context) (*PorchServer, error) {
224227
c.ExtraConfig.CacheOptions.ExternalRepoOptions.UserInfoProvider = userInfoProvider
225228
c.ExtraConfig.CacheOptions.ExternalRepoOptions.RepoOperationRetryAttempts = c.ExtraConfig.CacheOptions.RepoOperationRetryAttempts
226229

227-
cacheImpl, err := cache.GetCacheImpl(ctx, c.ExtraConfig.CacheOptions)
230+
var cacheImpl cachetypes.Cache
231+
err = retry.OnError(
232+
wait.Backoff{Duration: time.Second, Factor: 1.5, Steps: 20, Cap: 30 * time.Second},
233+
func(err error) bool {
234+
klog.Warningf("failed to create repository cache: %v; wait a sec...", err)
235+
return true
236+
},
237+
func() error {
238+
var err error
239+
cacheImpl, err = cache.GetCacheImpl(ctx, c.ExtraConfig.CacheOptions)
240+
return err
241+
})
228242

229243
if err != nil {
230-
return nil, fmt.Errorf("failed to create repository cache: %w", err)
244+
return nil, pkgerrors.Wrap(err, "failed to create repository cache")
231245
}
232246

233247
runnerOptionsResolver := func(namespace string) runneroptions.RunnerOptions {

0 commit comments

Comments
 (0)