Skip to content

Commit a3642a6

Browse files
author
Danil-Grigorev
committed
Write rendered manifests to destination bootstrap dir
1 parent 82592ee commit a3642a6

3 files changed

Lines changed: 206 additions & 28 deletions

File tree

pkg/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func ComposeConfig(platform configv1.PlatformType, imagesFile, managedNamespace
108108
func ComposeBootstrapConfig(infra *configv1.Infrastructure, imagesConfig *corev1.ConfigMap, managedNamespace string) (Config, error) {
109109
platform, err := GetProviderFromInfrastructure(infra)
110110
if err != nil {
111-
klog.Errorf("Unable to detemine platform from cluster infrastrucutre file %q: %s", client.ObjectKeyFromObject(infra), err)
111+
klog.Errorf("Unable to detemine platform from cluster infrastrucutre file: %s", err)
112112
return Config{}, err
113113
}
114114

pkg/render/render.go

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,38 @@ package render
22

33
import (
44
"bytes"
5+
"fmt"
6+
"io/fs"
57
"io/ioutil"
8+
"os"
9+
"path/filepath"
10+
"strings"
611

712
configv1 "github.com/openshift/api/config/v1"
813
"github.com/openshift/cluster-cloud-controller-manager-operator/pkg/cloud"
914
"github.com/openshift/cluster-cloud-controller-manager-operator/pkg/config"
1015
"github.com/openshift/cluster-cloud-controller-manager-operator/pkg/substitution"
1116
corev1 "k8s.io/api/core/v1"
12-
"k8s.io/apimachinery/pkg/util/yaml"
17+
"k8s.io/apimachinery/pkg/runtime"
18+
"k8s.io/apimachinery/pkg/runtime/serializer/json"
19+
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
1320
"k8s.io/klog"
1421
"sigs.k8s.io/controller-runtime/pkg/client"
1522
)
1623

1724
const (
1825
bootstrapNamespace = "openshift-cloud-controller-manager"
26+
bootstrapPrefix = "bootstrap"
27+
// bootstrapFileName is built from bootstrapPrefix, resource kind and name
28+
bootstrapFileName = "%s/%s-%s.yaml"
1929
)
2030

31+
var scheme *runtime.Scheme
32+
33+
func init() {
34+
scheme = runtime.NewScheme()
35+
}
36+
2137
// Render defines render config for use in bootstrap mode
2238
type Render struct {
2339
// dir where CCCMO reads the cloud configs, images config maps
@@ -43,6 +59,7 @@ func (r *Render) Run(destinationDir string) error {
4359
infra, imagesMap, err := r.readAssets()
4460
if err != nil {
4561
klog.Errorf("Cannot read assets from provided paths: %v", err)
62+
return err
4663
}
4764
config, err := config.ComposeBootstrapConfig(infra, imagesMap, bootstrapNamespace)
4865
if err != nil {
@@ -57,8 +74,7 @@ func (r *Render) Run(destinationDir string) error {
5774
klog.Infof("Collected resource %s %q successfully", resource.GetObjectKind().GroupVersionKind(), client.ObjectKeyFromObject(resource))
5875
}
5976

60-
// TODO: write resourcs on disk
61-
return nil
77+
return writeAssets(destinationDir, resources)
6278
}
6379

6480
// readAssets collects infrastructure resource and images config map from provided paths
@@ -70,7 +86,7 @@ func (r *Render) readAssets() (*configv1.Infrastructure, *corev1.ConfigMap, erro
7086
}
7187

7288
infra := &configv1.Infrastructure{}
73-
dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(infraData), 1000)
89+
dec := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(infraData), 1000)
7490
if err := dec.Decode(infra); err != nil {
7591
klog.Errorf("Cannot decode data into configv1.Infrastructure from %q: %v", r.infrastructureFile, err)
7692
return nil, nil, err
@@ -83,11 +99,33 @@ func (r *Render) readAssets() (*configv1.Infrastructure, *corev1.ConfigMap, erro
8399
}
84100

85101
imagesConfigMap := &corev1.ConfigMap{}
86-
dec = yaml.NewYAMLOrJSONDecoder(bytes.NewReader(imagesData), 1000)
102+
dec = k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(imagesData), 1000)
87103
if err := dec.Decode(imagesConfigMap); err != nil {
88104
klog.Errorf("Cannot decode data into v1.ConfigMap from %q: %v", r.imagesFile, err)
89105
return nil, nil, err
90106
}
91107

92108
return infra, imagesConfigMap, nil
93109
}
110+
111+
func writeAssets(destinationDir string, resources []client.Object) error {
112+
for _, resource := range resources {
113+
filename := fmt.Sprintf(bootstrapFileName, bootstrapPrefix, strings.ToLower(resource.GetObjectKind().GroupVersionKind().Kind), resource.GetName())
114+
klog.Infof("Writing file %q on disc", filename)
115+
116+
path := filepath.Join(destinationDir, filename)
117+
dirname := filepath.Dir(path)
118+
if err := os.MkdirAll(dirname, fs.ModePerm); err != nil {
119+
klog.Errorf("Unable to create destination dir %q: %v", dirname, err)
120+
return err
121+
}
122+
file, err := os.Create(path)
123+
if err != nil {
124+
klog.Errorf("Failed to open %q: %v", path, err)
125+
return err
126+
}
127+
defer file.Close()
128+
json.NewYAMLSerializer(json.DefaultMetaFactory, scheme, scheme).Encode(resource, file)
129+
}
130+
return nil
131+
}

pkg/render/render_test.go

Lines changed: 162 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package render
22

33
import (
4+
"fmt"
5+
"io/fs"
46
"io/ioutil"
57
"os"
8+
"path"
9+
"strings"
610
"testing"
711

812
configv1 "github.com/openshift/api/config/v1"
13+
"github.com/openshift/cluster-cloud-controller-manager-operator/pkg/cloud/aws"
914
"github.com/stretchr/testify/assert"
1015
corev1 "k8s.io/api/core/v1"
1116
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
"sigs.k8s.io/controller-runtime/pkg/client"
1218
)
1319

1420
const (
@@ -21,6 +27,13 @@ status:
2127
platform: AWS
2228
platformStatus:
2329
type: AWS
30+
`
31+
infraMissingPlatform = `apiVersion: config.openshift.io/v1
32+
kind: Infrastructure
33+
metadata:
34+
name: cluster
35+
spec: {}
36+
status: {}
2437
`
2538
imagesConfigMap = `apiVersion: v1
2639
kind: ConfigMap
@@ -85,28 +98,23 @@ func TestReadAssets(t *testing.T) {
8598
infra: matchingInfra,
8699
imagesContent: imagesConfigMap,
87100
imagesConfigMap: matchingConfigMap,
88-
},
89-
{
90-
name: "Infrastructure not located",
91-
expectError: "open not_found: no such file or directory",
92-
},
93-
{
94-
name: "ImagesConfigMap not located",
95-
infraContent: infra,
96-
expectError: "open not_found: no such file or directory",
97-
},
98-
{
99-
name: "Bad images config map file content",
100-
infraContent: "BAD",
101-
expectError: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type v1.Infrastructure",
102-
},
103-
{
104-
name: "Bad infrastructure file content",
105-
infraContent: infra,
106-
imagesContent: "BAD",
107-
expectError: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type v1.ConfigMap",
108-
},
109-
}
101+
}, {
102+
name: "Infrastructure not located",
103+
expectError: "open not_found: no such file or directory",
104+
}, {
105+
name: "ImagesConfigMap not located",
106+
infraContent: infra,
107+
expectError: "open not_found: no such file or directory",
108+
}, {
109+
name: "Bad images config map file content",
110+
infraContent: "BAD",
111+
expectError: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type v1.Infrastructure",
112+
}, {
113+
name: "Bad infrastructure file content",
114+
infraContent: infra,
115+
imagesContent: "BAD",
116+
expectError: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type v1.ConfigMap",
117+
}}
110118

111119
infraPath := "infra.yaml"
112120
configPath := "imagesConfigMap.yaml"
@@ -150,3 +158,135 @@ func TestReadAssets(t *testing.T) {
150158
})
151159
}
152160
}
161+
162+
func TestRenderRun(t *testing.T) {
163+
tc := []struct {
164+
name string
165+
infraContent string
166+
imagesContent string
167+
expectObjects []client.Object
168+
expectError string
169+
}{{
170+
name: "Unmarshal both infrastructure and images with no issue",
171+
infraContent: infra,
172+
imagesContent: imagesConfigMap,
173+
expectObjects: aws.GetBootstrapResources(),
174+
}, {
175+
name: "Infrastructure not populated",
176+
infraContent: infraMissingPlatform,
177+
imagesContent: imagesConfigMap,
178+
expectError: "platform status is not pupulated on infrastructure",
179+
}, {
180+
name: "ImagesConfigMap not located",
181+
infraContent: infra,
182+
imagesContent: "BAD",
183+
expectError: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type v1.ConfigMap",
184+
}}
185+
186+
infraPath := "infra.yaml"
187+
configPath := "imagesConfigMap.yaml"
188+
189+
for _, tc := range tc {
190+
t.Run(tc.name, func(t *testing.T) {
191+
imagesFile := "not_found"
192+
infrastructureFile := "not_found"
193+
destination, err := ioutil.TempDir("", "test")
194+
assert.NoError(t, err)
195+
196+
if tc.imagesContent != "" {
197+
file, err := ioutil.TempFile(os.TempDir(), configPath)
198+
path := file.Name()
199+
assert.NoError(t, err)
200+
defer file.Close()
201+
202+
_, err = file.WriteString(tc.imagesContent)
203+
assert.NoError(t, err)
204+
imagesFile = path
205+
}
206+
207+
if tc.infraContent != "" {
208+
file, err := ioutil.TempFile(os.TempDir(), infraPath)
209+
path := file.Name()
210+
assert.NoError(t, err)
211+
defer file.Close()
212+
213+
_, err = file.WriteString(tc.infraContent)
214+
assert.NoError(t, err)
215+
infrastructureFile = path
216+
}
217+
218+
r := New("", infrastructureFile, imagesFile)
219+
err = r.Run(destination)
220+
if tc.expectError != "" {
221+
assert.EqualError(t, err, tc.expectError)
222+
return
223+
}
224+
assert.NoError(t, err)
225+
226+
// Assert all files were written to bootstrap dir
227+
files, err := ioutil.ReadDir(path.Join(destination, bootstrapPrefix))
228+
assert.NoError(t, err)
229+
assert.Len(t, files, len(tc.expectObjects))
230+
})
231+
}
232+
}
233+
234+
func TestWriteAssets(t *testing.T) {
235+
tc := []struct {
236+
name string
237+
destination string
238+
preCreateMode fs.FileMode
239+
objects []client.Object
240+
expectErr string
241+
}{{
242+
name: "Writing file finished with success",
243+
destination: "test",
244+
objects: []client.Object{matchingInfra, matchingConfigMap},
245+
}, {
246+
name: "Fail to write into /dev/null",
247+
objects: []client.Object{matchingInfra, matchingConfigMap},
248+
expectErr: "mkdir /dev/null: not a directory",
249+
}, {
250+
name: "Fail to write into /dev/null",
251+
objects: []client.Object{matchingInfra, matchingConfigMap},
252+
destination: "bad_permissions",
253+
preCreateMode: 0444,
254+
expectErr: "permission denied",
255+
}}
256+
257+
for _, tc := range tc {
258+
t.Run(tc.name, func(t *testing.T) {
259+
destination := "/dev/null"
260+
261+
if tc.destination != "" {
262+
dirPath, err := ioutil.TempDir("", tc.destination)
263+
assert.NoError(t, err)
264+
destination = dirPath
265+
if tc.preCreateMode != 0 {
266+
os.MkdirAll(path.Join(destination, bootstrapPrefix), tc.preCreateMode)
267+
assert.NoError(t, err)
268+
}
269+
}
270+
271+
err := writeAssets(destination, tc.objects)
272+
if tc.expectErr != "" {
273+
assert.Contains(t, err.Error(), tc.expectErr)
274+
return
275+
}
276+
assert.NoError(t, err)
277+
278+
// Assert all files were written to bootstrap dir
279+
files, err := ioutil.ReadDir(path.Join(destination, bootstrapPrefix))
280+
assert.NoError(t, err)
281+
assert.Len(t, files, len(tc.objects))
282+
283+
names := []string{}
284+
for _, file := range files {
285+
names = append(names, file.Name())
286+
}
287+
for _, res := range tc.objects {
288+
assert.Contains(t, names, fmt.Sprintf("%s-%s.yaml", strings.ToLower(res.GetObjectKind().GroupVersionKind().Kind), res.GetName()))
289+
}
290+
})
291+
}
292+
}

0 commit comments

Comments
 (0)