55
66package io .stackgres .operator .conciliation .dbops ;
77
8+ import static io .stackgres .common .StackGresUtil .getPostgresFlavorComponent ;
9+
810import java .util .List ;
11+ import java .util .Map ;
12+ import java .util .Objects ;
913import java .util .Optional ;
14+ import java .util .function .Function ;
15+ import java .util .stream .Stream ;
1016
17+ import com .google .common .collect .ImmutableMap ;
1118import io .fabric8 .kubernetes .api .model .HasMetadata ;
1219import io .fabric8 .kubernetes .api .model .ObjectMeta ;
20+ import io .fabric8 .kubernetes .api .model .OwnerReference ;
21+ import io .stackgres .common .DbOpsUtil ;
22+ import io .stackgres .common .StackGresComponent ;
23+ import io .stackgres .common .StackGresVersion ;
1324import io .stackgres .common .crd .sgcluster .StackGresCluster ;
25+ import io .stackgres .common .crd .sgcluster .StackGresClusterDbOpsMajorVersionUpgradeStatus ;
26+ import io .stackgres .common .crd .sgcluster .StackGresClusterDbOpsStatus ;
1427import io .stackgres .common .crd .sgcluster .StackGresClusterSpec ;
28+ import io .stackgres .common .crd .sgcluster .StackGresClusterStatus ;
1529import io .stackgres .common .crd .sgconfig .StackGresConfig ;
1630import io .stackgres .common .crd .sgdbops .StackGresDbOps ;
1731import io .stackgres .common .crd .sgdbops .StackGresDbOpsBenchmark ;
2034import io .stackgres .common .crd .sgdbops .StackGresDbOpsSamplingStatus ;
2135import io .stackgres .common .crd .sgdbops .StackGresDbOpsSpec ;
2236import io .stackgres .common .crd .sgdbops .StackGresDbOpsStatus ;
37+ import io .stackgres .common .crd .sgdistributedlogs .StackGresDistributedLogs ;
38+ import io .stackgres .common .crd .sgpgconfig .StackGresPostgresConfig ;
2339import io .stackgres .common .crd .sgprofile .StackGresProfile ;
2440import io .stackgres .common .resource .CustomResourceFinder ;
2541import io .stackgres .common .resource .CustomResourceScanner ;
2642import io .stackgres .operator .conciliation .RequiredResourceGenerator ;
2743import io .stackgres .operator .conciliation .ResourceGenerationDiscoverer ;
2844import jakarta .enterprise .context .ApplicationScoped ;
2945import jakarta .inject .Inject ;
46+ import org .jooq .lambda .Seq ;
47+ import org .jooq .lambda .tuple .Tuple2 ;
3048import org .slf4j .Logger ;
3149import org .slf4j .LoggerFactory ;
3250
@@ -37,10 +55,21 @@ public class DbOpsRequiredResourcesGenerator
3755 protected static final Logger LOGGER = LoggerFactory
3856 .getLogger (DbOpsRequiredResourcesGenerator .class );
3957
58+ private final Map <StackGresComponent , Map <StackGresVersion , List <String >>> supportedPostgresVersions =
59+ Stream .of (StackGresComponent .POSTGRESQL , StackGresComponent .BABELFISH )
60+ .collect (ImmutableMap .toImmutableMap (Function .identity (),
61+ component -> component .getComponentVersions ()
62+ .entrySet ()
63+ .stream ()
64+ .collect (ImmutableMap .toImmutableMap (Map .Entry ::getKey ,
65+ entry -> entry .getValue ().streamOrderedVersions ().toList ()))));
66+
4067 private final CustomResourceScanner <StackGresConfig > configScanner ;
4168
4269 private final CustomResourceFinder <StackGresCluster > clusterFinder ;
4370
71+ private final CustomResourceFinder <StackGresPostgresConfig > postgresConfigFinder ;
72+
4473 private final CustomResourceFinder <StackGresProfile > profileFinder ;
4574
4675 private final CustomResourceFinder <StackGresDbOps > dbOpsFinder ;
@@ -51,11 +80,13 @@ public class DbOpsRequiredResourcesGenerator
5180 public DbOpsRequiredResourcesGenerator (
5281 CustomResourceScanner <StackGresConfig > configScanner ,
5382 CustomResourceFinder <StackGresCluster > clusterFinder ,
83+ CustomResourceFinder <StackGresPostgresConfig > postgresConfigFinder ,
5484 CustomResourceFinder <StackGresProfile > profileFinder ,
5585 CustomResourceFinder <StackGresDbOps > dbOpsFinder ,
5686 ResourceGenerationDiscoverer <StackGresDbOpsContext > discoverer ) {
5787 this .configScanner = configScanner ;
5888 this .clusterFinder = clusterFinder ;
89+ this .postgresConfigFinder = postgresConfigFinder ;
5990 this .profileFinder = profileFinder ;
6091 this .dbOpsFinder = dbOpsFinder ;
6192 this .discoverer = discoverer ;
@@ -74,15 +105,107 @@ public List<HasMetadata> getRequiredResources(StackGresDbOps dbOps) {
74105 .orElseThrow (() -> new IllegalArgumentException (
75106 "SGConfig not found or more than one exists. Aborting reoconciliation!" ));
76107
77- final StackGresDbOpsSpec spec = dbOps .getSpec ();
78- final Optional <StackGresCluster > cluster = clusterFinder
79- .findByNameAndNamespace (spec . getSgCluster () , dbOpsNamespace );
108+ final String clusterName = dbOps .getSpec (). getSgCluster ();
109+ final Optional <StackGresCluster > foundCluster = clusterFinder
110+ .findByNameAndNamespace (clusterName , dbOpsNamespace );
80111
81- final Optional <StackGresProfile > profile = cluster
112+ final Optional <StackGresProfile > foundProfile = foundCluster
82113 .map (StackGresCluster ::getSpec )
83114 .map (StackGresClusterSpec ::getSgInstanceProfile )
84115 .flatMap (profileName -> profileFinder
85116 .findByNameAndNamespace (profileName , dbOpsNamespace ));
117+ if (!DbOpsUtil .isAlreadyCompleted (dbOps )) {
118+ if (foundCluster .isEmpty ()) {
119+ throw new IllegalArgumentException (
120+ StackGresCluster .KIND + " " + clusterName + " was not found" );
121+ }
122+ final StackGresCluster cluster = foundCluster .get ();
123+
124+ if (foundProfile .isEmpty ()) {
125+ throw new IllegalArgumentException (
126+ StackGresProfile .KIND + " " + foundCluster .get ().getSpec ().getSgInstanceProfile () + " was not found" );
127+ }
128+
129+ if (dbOps .getSpec ().isOpMajorVersionUpgrade ()) {
130+ var foundOwnerReference = Optional .of (cluster )
131+ .map (StackGresCluster ::getMetadata )
132+ .map (ObjectMeta ::getOwnerReferences )
133+ .stream ()
134+ .flatMap (List ::stream )
135+ .filter (ownerReference -> !Objects .equals (
136+ ownerReference .getKind (),
137+ HasMetadata .getKind (StackGresDistributedLogs .class )))
138+ .filter (ownerReference -> ownerReference .getController () != null
139+ && ownerReference .getController ())
140+ .findFirst ();
141+ if (foundOwnerReference .isPresent ()) {
142+ OwnerReference ownerReference = foundOwnerReference .get ();
143+ throw new IllegalArgumentException (
144+ "Can not perform major version upgrade on " + StackGresCluster .KIND + " managed by "
145+ + ownerReference .getKind () + " " + ownerReference .getName ());
146+ }
147+
148+ final String givenPgVersion = dbOps .getSpec ().getMajorVersionUpgrade ().getPostgresVersion ();
149+ if (givenPgVersion != null
150+ && !isPostgresVersionSupported (cluster , givenPgVersion )) {
151+ final String message = "Unsupported postgres version " + givenPgVersion
152+ + ". Supported postgres versions are: "
153+ + Seq .seq (supportedPostgresVersions .get (getPostgresFlavorComponent (cluster ))
154+ .get (StackGresVersion .getStackGresVersion (cluster ))).toString (", " );
155+ throw new IllegalArgumentException (message );
156+ }
157+
158+ String givenMajorVersion = getPostgresFlavorComponent (cluster )
159+ .get (cluster ).getMajorVersion (givenPgVersion );
160+ long givenMajorVersionIndex = getPostgresFlavorComponent (cluster )
161+ .get (cluster )
162+ .streamOrderedMajorVersions ()
163+ .zipWithIndex ()
164+ .filter (t -> t .v1 .equals (givenMajorVersion ))
165+ .map (Tuple2 ::v2 )
166+ .findAny ()
167+ .orElseThrow ();
168+ String oldPgVersion = Optional .ofNullable (cluster .getStatus ())
169+ .map (StackGresClusterStatus ::getDbOps )
170+ .map (StackGresClusterDbOpsStatus ::getMajorVersionUpgrade )
171+ .map (StackGresClusterDbOpsMajorVersionUpgradeStatus ::getSourcePostgresVersion )
172+ .orElse (cluster .getSpec ().getPostgres ().getVersion ());
173+ String oldMajorVersion = getPostgresFlavorComponent (cluster )
174+ .get (cluster )
175+ .getMajorVersion (oldPgVersion );
176+ long oldMajorVersionIndex = getPostgresFlavorComponent (cluster )
177+ .get (cluster )
178+ .streamOrderedMajorVersions ()
179+ .zipWithIndex ()
180+ .filter (t -> t .v1 .equals (oldMajorVersion ))
181+ .map (Tuple2 ::v2 )
182+ .findAny ()
183+ .orElseThrow ();
184+
185+ if (givenMajorVersionIndex >= oldMajorVersionIndex ) {
186+ throw new IllegalArgumentException (
187+ "postgres version must be a newer major version than the current one ("
188+ + givenMajorVersion + " < " + oldMajorVersion + ")" );
189+ }
190+
191+ Optional <StackGresPostgresConfig > postgresConfig = postgresConfigFinder
192+ .findByNameAndNamespace (
193+ dbOps .getSpec ().getMajorVersionUpgrade ().getSgPostgresConfig (),
194+ dbOps .getMetadata ().getNamespace ());
195+ if (postgresConfig .isPresent ()) {
196+ if (!postgresConfig .get ().getSpec ().getPostgresVersion ().equals (givenMajorVersion )) {
197+ throw new IllegalArgumentException (
198+ StackGresPostgresConfig .KIND + " must be for postgres version "
199+ + givenMajorVersion + " but was for version "
200+ + postgresConfig .get ().getSpec ().getPostgresVersion ());
201+ }
202+ } else {
203+ throw new IllegalArgumentException (
204+ StackGresPostgresConfig .KIND + " "
205+ + dbOps .getSpec ().getMajorVersionUpgrade ().getSgPostgresConfig () + " not found" );
206+ }
207+ }
208+ }
86209
87210 final Optional <StackGresDbOpsSamplingStatus > samplingStatus = Optional .of (dbOps .getSpec ())
88211 .map (StackGresDbOpsSpec ::getBenchmark )
@@ -94,18 +217,24 @@ public List<HasMetadata> getRequiredResources(StackGresDbOps dbOps) {
94217 .map (StackGresDbOpsStatus ::getBenchmark )
95218 .map (StackGresDbOpsBenchmarkStatus ::getSampling )
96219 .orElseThrow (() -> new IllegalArgumentException (
97- "SGDbOps " + samplingDbOpsName
220+ StackGresDbOps . KIND + " " + samplingDbOpsName
98221 + " was not found or has no has no sampling status" )));
99222
100223 StackGresDbOpsContext context = ImmutableStackGresDbOpsContext .builder ()
101224 .config (config )
102225 .source (dbOps )
103- .foundCluster (cluster )
104- .foundProfile (profile )
226+ .foundCluster (foundCluster )
227+ .foundProfile (foundProfile )
105228 .samplingStatus (samplingStatus )
106229 .build ();
107230
108231 return discoverer .generateResources (context );
109232 }
110233
234+ private boolean isPostgresVersionSupported (StackGresCluster cluster , String version ) {
235+ return supportedPostgresVersions .get (getPostgresFlavorComponent (cluster ))
236+ .get (StackGresVersion .getStackGresVersion (cluster ))
237+ .contains (version );
238+ }
239+
111240}
0 commit comments