Skip to content

Commit 971582e

Browse files
authored
PR #60 follow-up fixes (#61)
1 parent e09164c commit 971582e

6 files changed

Lines changed: 544 additions & 506 deletions

File tree

docs/cluster-connection.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ The referenced secret must be of type `kubernetes.io/basic-auth` and contain the
3535

3636
Use this option when the credentials are mounted as a file instead of a Kubernetes Secret.
3737

38-
### File format
38+
#### File format
3939

4040
The file must contain JSON with the following fields:
4141

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
1-
package it.aboutbits.postgresql.core;
2-
3-
import io.fabric8.generator.annotation.Required;
4-
import io.fabric8.generator.annotation.ValidationRule;
5-
import lombok.Getter;
6-
import lombok.Setter;
7-
import org.jspecify.annotations.NullMarked;
8-
9-
/// A reference to a file inside the operator container.
10-
///
11-
/// This class is used wherever a CRD spec needs to point to a specific file
12-
/// The [#path] field identifies the file location within the container.
13-
///
14-
/// ### Example usage in a CR manifest
15-
///
16-
/// ```yaml
17-
/// spec:
18-
/// adminSecretFileRef:
19-
/// path: "/mnt/secrets/db-credentials.json"
20-
/// ```
21-
@Getter
22-
@Setter
23-
@NullMarked
24-
public class FileRef {
25-
/// The path to the file.
26-
/// Must not be blank.
27-
@Required
28-
@ValidationRule(
29-
value = "self.trim().size() > 0",
30-
message = "The path must not be empty."
31-
)
32-
private String path = "";
33-
}
1+
package it.aboutbits.postgresql.core;
2+
3+
import io.fabric8.generator.annotation.Required;
4+
import io.fabric8.generator.annotation.ValidationRule;
5+
import lombok.Getter;
6+
import lombok.Setter;
7+
import org.jspecify.annotations.NullMarked;
8+
9+
/// A reference to a file inside the operator container.
10+
///
11+
/// This class is used wherever a CRD spec needs to point to a specific file
12+
/// The [#path] field identifies the file location within the container.
13+
@Getter
14+
@Setter
15+
@NullMarked
16+
public class FileRef {
17+
/// The path to the file.
18+
/// Must not be blank.
19+
@Required
20+
@ValidationRule(
21+
value = "self.trim().size() > 0",
22+
message = "The path must not be empty."
23+
)
24+
private String path = "";
25+
}

operator/src/main/java/it/aboutbits/postgresql/core/KubernetesService.java

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package it.aboutbits.postgresql.core;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
34
import com.fasterxml.jackson.databind.ObjectMapper;
45
import io.fabric8.kubernetes.client.KubernetesClient;
56
import it.aboutbits.postgresql.crd.clusterconnection.ClusterConnection;
@@ -19,18 +20,12 @@
1920
@RequiredArgsConstructor
2021
@NullMarked
2122
public final class KubernetesService {
22-
private final ObjectMapper objectMapper;
23-
24-
private record FileCredentials(
25-
@Nullable String username,
26-
@Nullable String password
27-
) {
28-
}
29-
3023
public static final String SECRET_TYPE_BASIC_AUTH = "kubernetes.io/basic-auth";
3124
public static final String SECRET_DATA_BASIC_AUTH_USERNAME_KEY = "username";
3225
public static final String SECRET_DATA_BASIC_AUTH_PASSWORD_KEY = "password";
3326

27+
private final ObjectMapper objectMapper;
28+
3429
public Credentials getAdminCredentials(
3530
KubernetesClient kubernetesClient,
3631
ClusterConnection clusterConnection
@@ -39,13 +34,15 @@ public Credentials getAdminCredentials(
3934
if (spec.getAdminSecretRef() != null) {
4035
var secretRef = spec.getAdminSecretRef();
4136
var defaultNamespace = clusterConnection.getMetadata().getNamespace();
37+
4238
var credentials = getSecretRefCredentials(kubernetesClient, secretRef, defaultNamespace);
4339
if (credentials.username() == null) {
4440
var secretNamespace = getSecretNamespace(secretRef, defaultNamespace);
4541
throw new IllegalStateException(
4642
"The Secret reference is missing required data username [secret.namespace=%s, secret.name=%s]".formatted(
4743
secretNamespace, secretRef.getName()));
4844
}
45+
4946
return credentials;
5047
} else if (spec.getAdminSecretFileRef() != null) {
5148
return getSecretFileRefCredentials(spec.getAdminSecretFileRef());
@@ -60,20 +57,17 @@ public Credentials getSecretFileRefCredentials(FileRef fileRef) {
6057
try (var in = Files.newInputStream(path)) {
6158
var file = objectMapper.readValue(in, FileCredentials.class);
6259
if (file.username() == null) {
63-
throw new IllegalStateException(
64-
"Credentials file is missing required field 'username' [path=%s]".formatted(path));
60+
throw new IllegalStateException("Credentials file is missing required field 'username' [path=%s]".formatted(path));
6561
}
6662
if (file.password() == null) {
67-
throw new IllegalStateException(
68-
"Credentials file is missing required field 'password' [path=%s]".formatted(path));
63+
throw new IllegalStateException("Credentials file is missing required field 'password' [path=%s]".formatted(path));
6964
}
65+
7066
return new Credentials(file.username(), file.password());
7167
} catch (NoSuchFileException e) {
72-
throw new IllegalStateException(
73-
"Credentials file not found [path=%s]".formatted(path), e);
68+
throw new IllegalStateException("Credentials file not found [path=%s]".formatted(path), e);
7469
} catch (IOException e) {
75-
throw new IllegalStateException(
76-
"Failed to read the credentials file [path=%s]".formatted(path), e);
70+
throw new IllegalStateException("Failed to read the credentials file [path=%s]".formatted(path), e);
7771
}
7872
}
7973

@@ -141,9 +135,21 @@ public Credentials getSecretRefCredentials(
141135
);
142136
}
143137

144-
private String getSecretNamespace(ResourceRef secretRef, String defaultNamespace) {
138+
private String getSecretNamespace(
139+
ResourceRef secretRef,
140+
String defaultNamespace
141+
) {
145142
return secretRef.getNamespace() != null
146143
? secretRef.getNamespace()
147144
: defaultNamespace;
148145
}
146+
147+
/// The JSON file may carry more keys than we need, for example, the AWS Secrets Manager
148+
/// format also has `engine`, `host`, `port` and `dbname`. Unknown keys are ignored.
149+
@JsonIgnoreProperties(ignoreUnknown = true)
150+
private record FileCredentials(
151+
@Nullable String username,
152+
@Nullable String password
153+
) {
154+
}
149155
}

operator/src/main/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionSpec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
@SchemaCustomizer(value = HostCustomizer.class, input = "host")
2222
@NullMarked
2323
@ValidationRule(
24-
value = "(has(self.adminSecretRef) ? 1 : 0) + (has(self.adminSecretFileRef) ? 1 : 0) == 1",
24+
value = "has(self.adminSecretRef) != has(self.adminSecretFileRef)",
2525
message = "Exactly one of 'adminSecretRef' or 'adminSecretFileRef' must be provided"
2626
)
2727
public class ClusterConnectionSpec {

0 commit comments

Comments
 (0)