Skip to content

Commit 768b17d

Browse files
committed
fix: use a static kopia version, configurable
using :latest was unadvisable due to instability but also kubernetes's pull policy for :latest images is Always, which puts more load on than necessary and can even hit rate limits
1 parent f03b78b commit 768b17d

10 files changed

Lines changed: 78 additions & 20 deletions

File tree

.github/workflows/integration.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ jobs:
123123
run: |
124124
docker pull minio/minio:latest
125125
docker pull minio/mc:latest
126-
docker pull kopia/kopia:latest
126+
docker pull kopia/kopia:0.22.3
127127
docker pull postgres:16
128128
docker pull postgres:16-alpine
129129
docker pull alpine:latest
130-
kind load docker-image minio/minio:latest minio/mc:latest kopia/kopia:latest postgres:16 postgres:16-alpine alpine:latest
130+
kind load docker-image minio/minio:latest minio/mc:latest kopia/kopia:0.22.3 postgres:16 postgres:16-alpine alpine:latest
131131
132132
- name: Pre-pull CNPG images
133133
if: matrix.needs_cnpg

operator.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ metadata:
111111
namespace: pgro-system
112112
data:
113113
maxConcurrentRestores: "2"
114+
kopiaImage: "kopia/kopia:0.22.3"
114115
---
115116
apiVersion: v1
116117
kind: Service

src/bin/operator.rs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use tower_http::trace::TraceLayer;
1616
use tracing::{debug, info, warn};
1717

1818
use postgres_restore_operator::{
19-
context::Context,
19+
context::{Context, DEFAULT_KOPIA_IMAGE},
2020
controllers,
2121
types::{PostgresPhysicalReplica, PostgresPhysicalRestore},
2222
};
@@ -50,13 +50,28 @@ fn extract_max_concurrent_restores(cm: &ConfigMap) -> usize {
5050
.unwrap_or(DEFAULT_MAX_CONCURRENT_RESTORES)
5151
}
5252

53-
async fn read_max_concurrent_restores(client: &Client, namespace: &str) -> usize {
53+
fn extract_kopia_image(cm: &ConfigMap) -> String {
54+
cm.data
55+
.as_ref()
56+
.and_then(|d| d.get("kopiaImage"))
57+
.filter(|v| !v.is_empty())
58+
.cloned()
59+
.unwrap_or_else(|| DEFAULT_KOPIA_IMAGE.to_string())
60+
}
61+
62+
async fn read_config(client: &Client, namespace: &str) -> (usize, String) {
5463
let api: Api<ConfigMap> = Api::namespaced(client.clone(), namespace);
5564
match api.get(CONFIGMAP_NAME).await {
56-
Ok(cm) => extract_max_concurrent_restores(&cm),
65+
Ok(cm) => (
66+
extract_max_concurrent_restores(&cm),
67+
extract_kopia_image(&cm),
68+
),
5769
Err(e) => {
58-
warn!(error = %e, "failed to read ConfigMap {CONFIGMAP_NAME}, defaulting to {DEFAULT_MAX_CONCURRENT_RESTORES}");
59-
DEFAULT_MAX_CONCURRENT_RESTORES
70+
warn!(error = %e, "failed to read ConfigMap {CONFIGMAP_NAME}, using defaults");
71+
(
72+
DEFAULT_MAX_CONCURRENT_RESTORES,
73+
DEFAULT_KOPIA_IMAGE.to_string(),
74+
)
6075
}
6176
}
6277
}
@@ -77,17 +92,22 @@ async fn main() -> anyhow::Result<()> {
7792
let client = Client::try_default().await?;
7893

7994
let namespace = operator_namespace();
80-
let max_concurrent_restores = read_max_concurrent_restores(&client, &namespace).await;
95+
let (max_concurrent_restores, kopia_image) = read_config(&client, &namespace).await;
8196

8297
info!(
8398
max_concurrent_restores,
99+
kopia_image,
84100
metrics_addr,
85101
operator_namespace = namespace,
86102
version = env!("CARGO_PKG_VERSION"),
87103
"starting postgres-restore-operator"
88104
);
89105

90-
let ctx = Arc::new(Context::new(client.clone(), max_concurrent_restores));
106+
let ctx = Arc::new(Context::new(
107+
client.clone(),
108+
max_concurrent_restores,
109+
kopia_image,
110+
));
91111

92112
// Heartbeat: a background task updates this timestamp every 5s.
93113
// If the runtime is deadlocked, the timestamp goes stale and /livez fails.
@@ -106,6 +126,7 @@ async fn main() -> anyhow::Result<()> {
106126
let config_watcher_config =
107127
Config::default().fields(&format!("metadata.name={CONFIGMAP_NAME}"));
108128
let max_concurrent_ref = ctx.max_concurrent_restores.clone();
129+
let kopia_image_ref = ctx.kopia_image.clone();
109130
tokio::spawn(async move {
110131
let stream = watcher::watcher(config_api, config_watcher_config);
111132
futures::pin_mut!(stream);
@@ -121,6 +142,17 @@ async fn main() -> anyhow::Result<()> {
121142
"max_concurrent_restores updated from ConfigMap"
122143
);
123144
}
145+
146+
let new_image = extract_kopia_image(&cm);
147+
let mut image = kopia_image_ref.write().unwrap();
148+
if *image != new_image {
149+
info!(
150+
old = %*image,
151+
new = new_image,
152+
"kopia_image updated from ConfigMap"
153+
);
154+
*image = new_image;
155+
}
124156
}
125157
Ok(watcher::Event::Delete(_)) => {
126158
let old_val =
@@ -132,6 +164,16 @@ async fn main() -> anyhow::Result<()> {
132164
"ConfigMap deleted, reverted max_concurrent_restores to default"
133165
);
134166
}
167+
168+
let mut image = kopia_image_ref.write().unwrap();
169+
if *image != DEFAULT_KOPIA_IMAGE {
170+
info!(
171+
old = %*image,
172+
new = DEFAULT_KOPIA_IMAGE,
173+
"ConfigMap deleted, reverted kopia_image to default"
174+
);
175+
*image = DEFAULT_KOPIA_IMAGE.to_string();
176+
}
135177
}
136178
Ok(watcher::Event::Init | watcher::Event::InitDone) => {}
137179
Err(e) => {

src/context.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
1-
use std::sync::Arc;
21
use std::sync::atomic::{AtomicUsize, Ordering};
2+
use std::sync::{Arc, RwLock};
33

44
use chrono::{DateTime, Utc};
55
use kube::Client;
6-
use tokio::sync::RwLock;
76

87
use crate::metrics::Metrics;
98

9+
pub const DEFAULT_KOPIA_IMAGE: &str = "kopia/kopia:0.22.3";
10+
1011
pub struct Context {
1112
pub client: Client,
1213
pub metrics: Metrics,
13-
pub restore_queue: Arc<RwLock<RestoreQueue>>,
14+
pub restore_queue: Arc<tokio::sync::RwLock<RestoreQueue>>,
1415
pub max_concurrent_restores: Arc<AtomicUsize>,
16+
pub kopia_image: Arc<RwLock<String>>,
1517
pub http_client: reqwest::Client,
1618
}
1719

1820
impl Context {
19-
pub fn new(client: Client, max_concurrent_restores: usize) -> Self {
21+
pub fn new(client: Client, max_concurrent_restores: usize, kopia_image: String) -> Self {
2022
Self {
2123
client,
2224
metrics: Metrics::new(),
23-
restore_queue: Arc::new(RwLock::new(RestoreQueue::default())),
25+
restore_queue: Arc::new(tokio::sync::RwLock::new(RestoreQueue::default())),
2426
max_concurrent_restores: Arc::new(AtomicUsize::new(max_concurrent_restores)),
27+
kopia_image: Arc::new(RwLock::new(kopia_image)),
2528
http_client: reqwest::Client::new(),
2629
}
2730
}
@@ -30,6 +33,10 @@ impl Context {
3033
self.max_concurrent_restores.load(Ordering::Relaxed)
3134
}
3235

36+
pub fn kopia_image(&self) -> String {
37+
self.kopia_image.read().unwrap().clone()
38+
}
39+
3340
/// Remove a restore from the queue, promote the next pending one if there
3441
/// is capacity, and update the related gauges. Returns the name of the
3542
/// promoted restore, if any.

src/controllers/replica.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,12 @@ pub async fn reconcile(replica: Arc<PostgresPhysicalReplica>, ctx: Arc<Context>)
474474
match jobs.get_opt(&snapshot_job_name).await? {
475475
None => {
476476
info!(replica = name, "creating snapshot list job");
477-
let job = build_snapshot_list_job(&replica, &snapshot_job_name, &namespace)?;
477+
let job = build_snapshot_list_job(
478+
&replica,
479+
&snapshot_job_name,
480+
&namespace,
481+
&ctx.kopia_image(),
482+
)?;
478483
jobs.create(&PostParams::default(), &job).await?;
479484
return Ok(Action::requeue(Duration::from_secs(10)));
480485
}

src/controllers/replica/resources.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub fn build_snapshot_list_job(
6060
replica: &PostgresPhysicalReplica,
6161
job_name: &str,
6262
namespace: &str,
63+
kopia_image: &str,
6364
) -> Result<Job> {
6465
let kopia_secret = &replica.spec.kopia_secret_ref;
6566
let replica_name = replica.name_any();
@@ -181,7 +182,7 @@ printf '{"id":"%s","size":%s}' "$ID" "$SIZE" > /dev/termination-log
181182
restart_policy: Some("Never".to_string()),
182183
containers: vec![Container {
183184
name: "snapshot-list".to_string(),
184-
image: Some("kopia/kopia:latest".to_string()),
185+
image: Some(kopia_image.to_string()),
185186
command: Some(vec!["/bin/sh".to_string(), "-c".to_string()]),
186187
args: Some(vec![script.to_string()]),
187188
env: Some(env_vars),

src/controllers/restore.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ async fn reconcile_restoring(
191191
let replicas: Api<PostgresPhysicalReplica> = Api::namespaced(client.clone(), namespace);
192192
let replica = replicas.get(&restore.spec.replica).await?;
193193

194-
let job = build_restore_job(restore, &job_name, namespace, &replica)?;
194+
let job =
195+
build_restore_job(restore, &job_name, namespace, &replica, &ctx.kopia_image())?;
195196
jobs.create(&PostParams::default(), &job).await?
196197
}
197198
};

src/controllers/restore/builders.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ pub fn build_restore_job(
213213
job_name: &str,
214214
namespace: &str,
215215
replica: &PostgresPhysicalReplica,
216+
kopia_image: &str,
216217
) -> Result<Job> {
217218
let kopia_secret = &replica.spec.kopia_secret_ref;
218219
let pvc_name = format!("{}-data", restore.name_any());
@@ -321,7 +322,7 @@ echo -n "$VERSION" > /dev/termination-log
321322

322323
containers: vec![Container {
323324
name: "restore".to_string(),
324-
image: Some("kopia/kopia:latest".to_string()),
325+
image: Some(kopia_image.to_string()),
325326
command: Some(vec!["/bin/sh".to_string(), "-c".to_string()]),
326327
args: Some(vec![restore_script.to_string()]),
327328
env: Some(

tests/fixtures/setup-kopia-repo.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ spec:
5353
mountPath: /pgdata
5454
containers:
5555
- name: create-snapshot
56-
image: kopia/kopia:latest
56+
image: kopia/kopia:0.22.3
5757
imagePullPolicy: IfNotPresent
5858
command: ["/bin/sh", "-c"]
5959
args:

tests/fixtures/setup-non-postgres-snapshot.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ spec:
2121
echo "Bucket created"
2222
containers:
2323
- name: create-snapshot
24-
image: kopia/kopia:latest
24+
image: kopia/kopia:0.22.3
2525
imagePullPolicy: IfNotPresent
2626
command: ["/bin/sh", "-c"]
2727
args:

0 commit comments

Comments
 (0)