@@ -11,25 +11,50 @@ import (
1111 "github.com/replicatedhq/kots/pkg/cursor"
1212 "github.com/replicatedhq/kots/pkg/kotsutil"
1313 "github.com/replicatedhq/kots/pkg/store"
14+ "github.com/replicatedhq/kots/pkg/update/types"
1415 upstreamtypes "github.com/replicatedhq/kots/pkg/upstream/types"
16+ "github.com/replicatedhq/kots/pkg/util"
1517 kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
1618)
1719
18- func isUpdateDeployable (updateCursor string , updates []upstreamtypes.Update ) (bool , string ) {
19- // iterate over updates in reverse since they are sorted in descending order
20+ // getAvailableUpdates returns the slice of available updates given a list of upstream updates making sure to:
21+ // - Check for previously required versions and setting the deployabled and cause properties accordingly
22+ // - Check for kubernetes version compatibility for embedded cluster versions and setting the deployable and cause properties accordingly
23+ func getAvailableUpdates (updates []upstreamtypes.Update , currentECVersion string ) []types.AvailableUpdate {
24+ availableUpdates := make ([]types.AvailableUpdate , len (updates ))
25+
26+ // keep the required updates in a slice in order to add these to the cause properties of each update
2027 requiredUpdates := []string {}
28+ // iterate over all updates in reverse since they are sorted in descending order
2129 for i := len (updates ) - 1 ; i >= 0 ; i -- {
22- if updates [i ].Cursor == updateCursor {
23- break
30+ upstreamUpdate := updates [i ]
31+ availableUpdates [i ] = types.AvailableUpdate {
32+ VersionLabel : upstreamUpdate .VersionLabel ,
33+ UpdateCursor : upstreamUpdate .Cursor ,
34+ ChannelID : upstreamUpdate .ChannelID ,
35+ IsRequired : upstreamUpdate .IsRequired ,
36+ UpstreamReleasedAt : upstreamUpdate .ReleasedAt ,
37+ ReleaseNotes : upstreamUpdate .ReleaseNotes ,
38+ IsDeployable : true ,
2439 }
25- if updates [i ].IsRequired {
26- requiredUpdates = append (requiredUpdates , updates [i ].VersionLabel )
40+ // if there's any required before the current update, mark it as non-deployable and set the cause
41+ if len (requiredUpdates ) > 0 {
42+ availableUpdates [i ].IsDeployable = false
43+ availableUpdates [i ].NonDeployableCause = getRequiredNonDeployableCause (requiredUpdates )
44+ // else check the k8s versions are compatible but only do so if the update has an embeded cluster version specificied
45+ } else if upstreamUpdate .EmbeddedClusterVersion != "" {
46+ if err := util .UpdateWithinKubeRange (currentECVersion , upstreamUpdate .EmbeddedClusterVersion ); err != nil {
47+ availableUpdates [i ].IsDeployable = false
48+ availableUpdates [i ].NonDeployableCause = getKubeVersionNonDeployableCause (err )
49+ }
50+ }
51+ // if this update is required add it to the slice so that we can mention it for the next updates
52+ if upstreamUpdate .IsRequired {
53+ requiredUpdates = append (requiredUpdates , upstreamUpdate .VersionLabel )
2754 }
2855 }
29- if len (requiredUpdates ) > 0 {
30- return false , getNonDeployableCause (requiredUpdates )
31- }
32- return true , ""
56+
57+ return availableUpdates
3358}
3459
3560func IsAirgapUpdateDeployable (app * apptypes.App , airgap * kotsv1beta1.Airgap ) (bool , string , error ) {
@@ -46,7 +71,7 @@ func IsAirgapUpdateDeployable(app *apptypes.App, airgap *kotsv1beta1.Airgap) (bo
4671 return false , "" , errors .Wrap (err , "failed to get missing required versions" )
4772 }
4873 if len (requiredUpdates ) > 0 {
49- return false , getNonDeployableCause (requiredUpdates ), nil
74+ return false , getRequiredNonDeployableCause (requiredUpdates ), nil
5075 }
5176 return true , "" , nil
5277}
@@ -105,7 +130,8 @@ func getRequiredAirgapUpdates(airgap *kotsv1beta1.Airgap, license *kotsv1beta1.L
105130 return requiredUpdates , nil
106131}
107132
108- func getNonDeployableCause (requiredUpdates []string ) string {
133+ // getRequiredNonDeployableCause constructs a non-deployable cause message based on the required updates.
134+ func getRequiredNonDeployableCause (requiredUpdates []string ) string {
109135 if len (requiredUpdates ) == 0 {
110136 return ""
111137 }
@@ -119,3 +145,16 @@ func getNonDeployableCause(requiredUpdates []string) string {
119145 }
120146 return fmt .Sprintf ("This version cannot be deployed because versions %s are required and must be deployed first." , versionLabelsStr )
121147}
148+
149+ // getKubeVersionNonDeployableCause constructs a non-deployable cause message based on the kube range validation error message
150+ func getKubeVersionNonDeployableCause (err error ) string {
151+ switch {
152+ case errors .Is (err , util .ErrKubeMinorRangeMismatch ):
153+ return "Before you can update to this version, you need to update to an earlier version that includes the required infrastructure update."
154+ case errors .Is (err , util .ErrKubeVersionDowngrade ):
155+ return "This version cannot be deployed because it would downgrade the infrastructure, which is unsupported."
156+ case errors .Is (err , util .ErrKubeMajorVersionUpgrade ):
157+ return "Release includes a major version upgrade of the infrastructure version, which is not allowed. Cannot use release."
158+ }
159+ return "This version cannot be deployed because the required infrastructure compatibility could not be verified."
160+ }
0 commit comments