@@ -6,6 +6,7 @@ package diff
6
6
7
7
import (
8
8
"bytes"
9
+ "context"
9
10
"encoding/json"
10
11
"errors"
11
12
"fmt"
@@ -20,6 +21,7 @@ import (
20
21
"k8s.io/apimachinery/pkg/util/managedfields"
21
22
"k8s.io/apimachinery/pkg/util/strategicpatch"
22
23
"k8s.io/client-go/kubernetes/scheme"
24
+ cmdutil "k8s.io/kubectl/pkg/cmd/util"
23
25
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
24
26
"sigs.k8s.io/structured-merge-diff/v4/merge"
25
27
"sigs.k8s.io/structured-merge-diff/v4/typed"
@@ -83,6 +85,14 @@ func Diff(config, live *unstructured.Unstructured, opts ...Option) (*DiffResult,
83
85
Normalize (live , opts ... )
84
86
}
85
87
88
+ if o .serverSideDiff {
89
+ r , err := ServerSideDiff (config , live , o .manager , o .kubeApplier , opts ... )
90
+ if err != nil {
91
+ return nil , fmt .Errorf ("error calculating server side diff: %w" , err )
92
+ }
93
+ return r , nil
94
+ }
95
+
86
96
// TODO The two variables bellow are necessary because there is a cyclic
87
97
// dependency with the kube package that blocks the usage of constants
88
98
// from common package. common package needs to be refactored and exclude
@@ -120,6 +130,47 @@ func Diff(config, live *unstructured.Unstructured, opts ...Option) (*DiffResult,
120
130
return TwoWayDiff (config , live )
121
131
}
122
132
133
+ func ServerSideDiff (config , live * unstructured.Unstructured , manager string , kubeApplier KubeApplier , opts ... Option ) (* DiffResult , error ) {
134
+ if live != nil && config != nil {
135
+ return serverSideDiff (config , live , manager , kubeApplier , opts ... )
136
+ }
137
+ return handleResourceCreateOrDeleteDiff (config , live )
138
+ }
139
+
140
+ func serverSideDiff (config , live * unstructured.Unstructured , manager string , kubeApplier KubeApplier , opts ... Option ) (* DiffResult , error ) {
141
+ predictedLiveStr , err := kubeApplier .ApplyResource (context .Background (), config , cmdutil .DryRunServer , false , false , true , manager )
142
+ if err != nil {
143
+ return nil , fmt .Errorf ("error running server side apply in dryrun mode: %w" , err )
144
+ }
145
+ predictedLive , err := jsonStrToUnstructured (predictedLiveStr )
146
+ if err != nil {
147
+ return nil , fmt .Errorf ("error converting json string to unstructured: %w" , err )
148
+ }
149
+
150
+ Normalize (predictedLive , opts ... )
151
+
152
+ predictedLiveBytes , err := json .Marshal (predictedLive )
153
+ if err != nil {
154
+ return nil , fmt .Errorf ("error marshaling predicted live resource: %w" , err )
155
+ }
156
+
157
+ unstructured .RemoveNestedField (live .Object , "metadata" , "managedFields" )
158
+ liveBytes , err := json .Marshal (live )
159
+ if err != nil {
160
+ return nil , fmt .Errorf ("error marshaling live resource: %w" , err )
161
+ }
162
+ return buildDiffResult (predictedLiveBytes , liveBytes ), nil
163
+ }
164
+
165
+ func jsonStrToUnstructured (jsonString string ) (* unstructured.Unstructured , error ) {
166
+ res := make (map [string ]interface {})
167
+ err := json .Unmarshal ([]byte (jsonString ), & res )
168
+ if err != nil {
169
+ return nil , fmt .Errorf ("unmarshal error: %s" , err )
170
+ }
171
+ return & unstructured.Unstructured {Object : res }, nil
172
+ }
173
+
123
174
// StructuredMergeDiff will calculate the diff using the structured-merge-diff
124
175
// k8s library (https://github.com/kubernetes-sigs/structured-merge-diff).
125
176
func StructuredMergeDiff (config , live * unstructured.Unstructured , gvkParser * managedfields.GvkParser , manager string ) (* DiffResult , error ) {
0 commit comments