Skip to content

Commit d41914c

Browse files
authored
fix: plumb skip existing images toggle to kotsadm pod (#5950)
* fix: plumb skip existing images toggle to kotsadm pod Signed-off-by: Evans Mungai <evans@replicated.com> * fix: add wget needed by wait-for-rqlite.sh to local image Signed-off-by: Evans Mungai <evans@replicated.com> --------- Signed-off-by: Evans Mungai <evans@replicated.com>
1 parent e1a3f42 commit d41914c

7 files changed

Lines changed: 62 additions & 33 deletions

File tree

dev/dockerfiles/kotsadm-migrations/Dockerfile.ttlsh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ENV DEBIAN_FRONTEND=noninteractive
1212
RUN apt-get update \
1313
&& apt-get install -y --no-install-recommends \
1414
ca-certificates \
15+
wget \
1516
&& apt-get install -y --only-upgrade --no-install-recommends \
1617
passwd login \
1718
&& apt-get clean \

pkg/airgap/airgap.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type CreateAirgapAppOpts struct {
3939
RegistryUsername string
4040
RegistryPassword string
4141
RegistryIsReadOnly bool
42+
SkipExistingImages bool
4243
IsAutomated bool
4344
ConfigValues string
4445
SkipPreflights bool
@@ -217,6 +218,7 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) {
217218
Password: opts.RegistryPassword,
218219
IsReadOnly: opts.RegistryIsReadOnly,
219220
},
221+
SkipExistingImages: opts.SkipExistingImages,
220222
AppID: opts.PendingApp.ID,
221223
AppSlug: opts.PendingApp.Slug,
222224
AppSequence: 0,

pkg/handlers/airgap.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import (
2626
)
2727

2828
type CreateAppFromAirgapRequest struct {
29-
RegistryHost string `json:"registryHost"`
30-
Namespace string `json:"namespace"`
31-
Username string `json:"username"`
32-
Password string `json:"password"`
33-
IsReadOnly bool `json:"isReadOnly"`
29+
RegistryHost string `json:"registryHost"`
30+
Namespace string `json:"namespace"`
31+
Username string `json:"username"`
32+
Password string `json:"password"`
33+
IsReadOnly bool `json:"isReadOnly"`
34+
SkipExistingImages bool `json:"skipExistingImages"`
3435
}
3536
type CreateAppFromAirgapResponse struct {
3637
}
@@ -415,6 +416,7 @@ func (h *Handler) CreateAppFromAirgap(w http.ResponseWriter, r *http.Request) {
415416
RegistryUsername: username,
416417
RegistryPassword: password,
417418
RegistryIsReadOnly: isReadOnly,
419+
SkipExistingImages: createAppFromAirgapRequest.SkipExistingImages,
418420
}
419421
if err := airgap.CreateAppFromAirgap(createAppOpts); err != nil {
420422
logger.Error(errors.Wrap(err, "failed to create app from airgap bundle"))

pkg/kotsadm/objects/scripts/wait-for-rqlite.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
timeout=300
99
elapsed=0
1010

11+
if ! command -v wget >/dev/null 2>&1; then
12+
echo "ERROR: wget is not installed in this image; cannot probe rqlite readiness" >&2
13+
exit 1
14+
fi
15+
1116
while [ $elapsed -lt $timeout ]; do
1217
if wget -qO- http://kotsadm-rqlite:4001/readyz 2>/dev/null | grep -q "ok"; then
1318
echo "rqlite is ready (${elapsed}s)"

pkg/pull/pull.go

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,25 @@ type PullOptions struct {
6363
Silent bool
6464
RewriteImages bool
6565
RewriteImageOptions registrytypes.RegistrySettings
66-
SkipHelmChartCheck bool
67-
ReportWriter io.Writer
68-
AppID string
69-
AppSlug string
70-
AppSequence int64
71-
AppVersionLabel string
72-
AppSelectedChannelID string
73-
IsGitOps bool
74-
StorageClassName string
75-
HTTPProxyEnvValue string
76-
HTTPSProxyEnvValue string
77-
NoProxyEnvValue string
78-
PrivateCAsConfigmap string
79-
ReportingInfo *reportingtypes.ReportingInfo
80-
SkipCompatibilityCheck bool
81-
KotsKinds *kotsutil.KotsKinds
66+
// SkipExistingImages, when true, makes each image push idempotent — see
67+
// imagetypes.CopyImageOptions.SkipExistingImages.
68+
SkipExistingImages bool
69+
SkipHelmChartCheck bool
70+
ReportWriter io.Writer
71+
AppID string
72+
AppSlug string
73+
AppSequence int64
74+
AppVersionLabel string
75+
AppSelectedChannelID string
76+
IsGitOps bool
77+
StorageClassName string
78+
HTTPProxyEnvValue string
79+
HTTPSProxyEnvValue string
80+
NoProxyEnvValue string
81+
PrivateCAsConfigmap string
82+
ReportingInfo *reportingtypes.ReportingInfo
83+
SkipCompatibilityCheck bool
84+
KotsKinds *kotsutil.KotsKinds
8285
}
8386

8487
var (
@@ -365,16 +368,17 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) {
365368
}
366369

367370
processImageOptions := imagetypes.ProcessImageOptions{
368-
AppSlug: pullOptions.AppSlug,
369-
Namespace: pullOptions.Namespace,
370-
RewriteImages: pullOptions.RewriteImages,
371-
RegistrySettings: pullOptions.RewriteImageOptions,
372-
CopyImages: !pullOptions.RewriteImageOptions.IsReadOnly,
373-
RootDir: pullOptions.RootDir,
374-
IsAirgap: pullOptions.IsAirgap,
375-
AirgapBundle: pullOptions.AirgapBundle,
376-
CreateAppDir: pullOptions.CreateAppDir,
377-
ReportWriter: pullOptions.ReportWriter,
371+
AppSlug: pullOptions.AppSlug,
372+
Namespace: pullOptions.Namespace,
373+
RewriteImages: pullOptions.RewriteImages,
374+
RegistrySettings: pullOptions.RewriteImageOptions,
375+
CopyImages: !pullOptions.RewriteImageOptions.IsReadOnly,
376+
RootDir: pullOptions.RootDir,
377+
IsAirgap: pullOptions.IsAirgap,
378+
AirgapBundle: pullOptions.AirgapBundle,
379+
CreateAppDir: pullOptions.CreateAppDir,
380+
ReportWriter: pullOptions.ReportWriter,
381+
SkipExistingImages: pullOptions.SkipExistingImages,
378382
}
379383

380384
if needsConfig {

web/src/components/UploadAirgapBundle.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class UploadAirgapBundle extends Component {
181181
username: this.state.registryDetails.username,
182182
password: this.state.registryDetails.password,
183183
isReadOnly: this.state.registryDetails.isReadOnly,
184+
skipExistingImages: this.state.registryDetails.skipExistingImages,
184185
simultaneousUploads: this.state.simultaneousUploads,
185186
};
186187
this.state.airgapUploader.upload(
@@ -217,6 +218,7 @@ class UploadAirgapBundle extends Component {
217218
password: fields.password,
218219
namespace: fields.namespace,
219220
isReadOnly: fields.isReadOnly,
221+
skipExistingImages: fields.skipExistingImages,
220222
},
221223
});
222224
};

web/src/components/shared/AirgapRegistrySettings.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ interface RegistryDetails {
3232

3333
interface GatherDetails extends RegistryDetails {
3434
isReadOnly: boolean;
35+
skipExistingImages: boolean;
3536
}
3637

3738
type State = {
@@ -235,14 +236,21 @@ class AirgapRegistrySettings extends Component<Props, State> {
235236
// @ts-expect-error
236237
this.setState(nextState, () => {
237238
if (this.props.gatherDetails) {
238-
const { hostname, username, password, namespace, isReadOnly } =
239-
this.state;
239+
const {
240+
hostname,
241+
username,
242+
password,
243+
namespace,
244+
isReadOnly,
245+
skipExistingImages,
246+
} = this.state;
240247
this.props.gatherDetails({
241248
hostname,
242249
username,
243250
password,
244251
namespace,
245252
isReadOnly,
253+
skipExistingImages,
246254
});
247255
}
248256
});
@@ -286,6 +294,9 @@ class AirgapRegistrySettings extends Component<Props, State> {
286294
password: result.password,
287295
namespace: result.namespace,
288296
isReadOnly: result.isReadOnly,
297+
// skipExistingImages is not persisted/returned by the registry GET
298+
// endpoint, so default to false to keep the checkbox controlled.
299+
skipExistingImages: result.skipExistingImages ?? false,
289300
loading: false,
290301
fetchRegistryErrMsg: "",
291302
displayErrorModal: false,
@@ -294,12 +305,14 @@ class AirgapRegistrySettings extends Component<Props, State> {
294305
if (this.props.gatherDetails) {
295306
const { hostname, username, password, namespace, isReadOnly } =
296307
result;
308+
const skipExistingImages = result.skipExistingImages ?? false;
297309
this.props.gatherDetails({
298310
hostname,
299311
username,
300312
password,
301313
namespace,
302314
isReadOnly,
315+
skipExistingImages,
303316
});
304317
}
305318
} else {

0 commit comments

Comments
 (0)