Skip to content

Commit 58bbbe2

Browse files
committed
Add image-based config ISO assets
Signed-off-by: Michail Resvanis <[email protected]>
1 parent ecfdaba commit 58bbbe2

18 files changed

+2050
-5
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ require (
128128
sigs.k8s.io/cluster-api-provider-vsphere v1.9.3
129129
sigs.k8s.io/controller-runtime v0.18.3
130130
sigs.k8s.io/controller-tools v0.12.0
131+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd
131132
sigs.k8s.io/yaml v1.4.0
132133
)
133134

@@ -292,7 +293,6 @@ require (
292293
k8s.io/component-base v0.30.1 // indirect
293294
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
294295
k8s.io/kubectl v0.30.1 // indirect
295-
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
296296
sigs.k8s.io/kustomize/api v0.16.0 // indirect
297297
sigs.k8s.io/kustomize/kyaml v0.16.0 // indirect
298298
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package configimage
2+
3+
import (
4+
"context"
5+
6+
"github.com/openshift/installer/pkg/asset"
7+
"github.com/openshift/installer/pkg/asset/tls"
8+
)
9+
10+
// ImageBasedKubeAPIServerCompleteCABundle is the asset the generates the kube-apiserver-complete-server-ca-bundle,
11+
// which contains all the certs that are valid to confirm the kube-apiserver identity and it also contains the
12+
// Ingress Operator CA certificate.
13+
type ImageBasedKubeAPIServerCompleteCABundle struct {
14+
tls.CertBundle
15+
}
16+
17+
var _ asset.Asset = (*ImageBasedKubeAPIServerCompleteCABundle)(nil)
18+
19+
// Dependencies returns the dependency of the cert bundle.
20+
func (a *ImageBasedKubeAPIServerCompleteCABundle) Dependencies() []asset.Asset {
21+
return []asset.Asset{
22+
&tls.KubeAPIServerLocalhostCABundle{},
23+
&tls.KubeAPIServerServiceNetworkCABundle{},
24+
&tls.KubeAPIServerLBCABundle{},
25+
&IngressOperatorCABundle{},
26+
}
27+
}
28+
29+
// Generate generates the cert bundle based on its dependencies.
30+
func (a *ImageBasedKubeAPIServerCompleteCABundle) Generate(ctx context.Context, deps asset.Parents) error {
31+
certs := []tls.CertInterface{}
32+
for _, asset := range a.Dependencies() {
33+
deps.Get(asset)
34+
certs = append(certs, asset.(tls.CertInterface))
35+
}
36+
return a.CertBundle.Generate(ctx, "kube-apiserver-complete-server-ca-bundle", certs...)
37+
}
38+
39+
// Name returns the human-friendly name of the asset.
40+
func (a *ImageBasedKubeAPIServerCompleteCABundle) Name() string {
41+
return "Certificate (kube-apiserver-complete-server-ca-bundle)"
42+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package configimage
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
10+
"github.com/openshift/installer/pkg/asset"
11+
"github.com/openshift/installer/pkg/asset/tls"
12+
)
13+
14+
func TestCaBundle_Generate(t *testing.T) {
15+
expectedBundleRaw := bytes.Join([][]byte{
16+
lbCABundle().BundleRaw,
17+
localhostCABundle().BundleRaw,
18+
serviceNetworkCABundle().BundleRaw,
19+
ingressCABundle().BundleRaw,
20+
}, []byte{})
21+
22+
cases := []struct {
23+
name string
24+
dependencies []asset.Asset
25+
expected *tls.CertBundle
26+
}{
27+
{
28+
name: "valid dependencies",
29+
dependencies: []asset.Asset{
30+
lbCABundle(),
31+
localhostCABundle(),
32+
serviceNetworkCABundle(),
33+
ingressCABundle(),
34+
},
35+
expected: &tls.CertBundle{
36+
BundleRaw: expectedBundleRaw,
37+
FileList: []*asset.File{
38+
{
39+
Filename: "tls/kube-apiserver-complete-server-ca-bundle.crt",
40+
Data: expectedBundleRaw,
41+
},
42+
},
43+
},
44+
},
45+
}
46+
for _, tc := range cases {
47+
t.Run(tc.name, func(t *testing.T) {
48+
parents := asset.Parents{}
49+
parents.Add(tc.dependencies...)
50+
51+
asset := &ImageBasedKubeAPIServerCompleteCABundle{}
52+
err := asset.Generate(context.TODO(), parents)
53+
assert.NoError(t, err)
54+
assert.Equal(t, string(tc.expected.BundleRaw), string(asset.CertBundle.BundleRaw))
55+
assert.Equal(t, tc.expected.FileList, asset.CertBundle.FileList)
56+
})
57+
}
58+
}
59+
60+
func lbCABundle() *tls.KubeAPIServerLBCABundle {
61+
return &tls.KubeAPIServerLBCABundle{
62+
CertBundle: tls.CertBundle{
63+
BundleRaw: []byte(testCert),
64+
},
65+
}
66+
}
67+
68+
func localhostCABundle() *tls.KubeAPIServerLocalhostCABundle {
69+
return &tls.KubeAPIServerLocalhostCABundle{
70+
CertBundle: tls.CertBundle{
71+
BundleRaw: []byte(testCert),
72+
},
73+
}
74+
}
75+
76+
func serviceNetworkCABundle() *tls.KubeAPIServerServiceNetworkCABundle {
77+
return &tls.KubeAPIServerServiceNetworkCABundle{
78+
CertBundle: tls.CertBundle{
79+
BundleRaw: []byte(testCert),
80+
},
81+
}
82+
}
83+
84+
func ingressCABundle() *IngressOperatorCABundle {
85+
return &IngressOperatorCABundle{
86+
CertBundle: tls.CertBundle{
87+
BundleRaw: []byte(testCert),
88+
},
89+
}
90+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package configimage
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
11+
k8sjson "sigs.k8s.io/json"
12+
13+
"github.com/openshift/installer/pkg/asset"
14+
"github.com/openshift/installer/pkg/asset/password"
15+
"github.com/openshift/installer/pkg/asset/tls"
16+
"github.com/openshift/installer/pkg/types"
17+
"github.com/openshift/installer/pkg/types/imagebased"
18+
)
19+
20+
const (
21+
defaultChronyConf = `
22+
pool 0.rhel.pool.ntp.org iburst
23+
driftfile /var/lib/chrony/drift
24+
makestep 1.0 3
25+
rtcsync
26+
logdir /var/log/chrony`
27+
28+
userCABundleConfigMapName = "user-ca-bundle"
29+
)
30+
31+
var (
32+
clusterConfigurationFilename = filepath.Join(clusterConfigurationDir, "manifest.json")
33+
34+
_ asset.WritableAsset = (*ClusterConfiguration)(nil)
35+
)
36+
37+
// ClusterConfiguration generates the image-based installer cluster configuration asset.
38+
type ClusterConfiguration struct {
39+
File *asset.File
40+
Config *imagebased.SeedReconfiguration
41+
}
42+
43+
// Name returns a human friendly name for the asset.
44+
func (*ClusterConfiguration) Name() string {
45+
return "Image-based installer cluster configuration"
46+
}
47+
48+
// Dependencies returns all of the dependencies directly needed to generate
49+
// the asset.
50+
func (*ClusterConfiguration) Dependencies() []asset.Asset {
51+
return []asset.Asset{
52+
&InstallConfig{},
53+
&ClusterID{},
54+
&tls.KubeAPIServerLBSignerCertKey{},
55+
&tls.KubeAPIServerLocalhostSignerCertKey{},
56+
&tls.KubeAPIServerServiceNetworkSignerCertKey{},
57+
&tls.AdminKubeConfigSignerCertKey{},
58+
&IngressOperatorSignerCertKey{},
59+
&password.KubeadminPassword{},
60+
&ImageBasedConfig{},
61+
}
62+
}
63+
64+
// Generate generates the Image-based Installer ClusterConfiguration manifest.
65+
func (cc *ClusterConfiguration) Generate(_ context.Context, dependencies asset.Parents) error {
66+
installConfig := &InstallConfig{}
67+
clusterID := &ClusterID{}
68+
imageBasedConfig := &ImageBasedConfig{}
69+
serverLBSignerCertKey := &tls.KubeAPIServerLBSignerCertKey{}
70+
serverLocalhostSignerCertKey := &tls.KubeAPIServerLocalhostSignerCertKey{}
71+
serverServiceNetworkSignerCertKey := &tls.KubeAPIServerServiceNetworkSignerCertKey{}
72+
adminKubeConfigSignerCertKey := &tls.AdminKubeConfigSignerCertKey{}
73+
ingressOperatorSignerCertKey := &IngressOperatorSignerCertKey{}
74+
75+
dependencies.Get(
76+
installConfig,
77+
clusterID,
78+
imageBasedConfig,
79+
serverLBSignerCertKey,
80+
serverLocalhostSignerCertKey,
81+
serverServiceNetworkSignerCertKey,
82+
adminKubeConfigSignerCertKey,
83+
ingressOperatorSignerCertKey,
84+
)
85+
86+
pwd := &password.KubeadminPassword{}
87+
dependencies.Get(pwd)
88+
pwdHash := string(pwd.PasswordHash)
89+
90+
if installConfig.Config == nil || imageBasedConfig.Config == nil {
91+
return cc.finish()
92+
}
93+
94+
cc.Config = &imagebased.SeedReconfiguration{
95+
APIVersion: imagebased.SeedReconfigurationVersion,
96+
BaseDomain: installConfig.Config.BaseDomain,
97+
ClusterID: clusterID.UUID,
98+
ClusterName: installConfig.ClusterName(),
99+
Hostname: imageBasedConfig.Config.Hostname,
100+
InfraID: clusterID.InfraID,
101+
KubeadminPasswordHash: pwdHash,
102+
Proxy: installConfig.Config.Proxy,
103+
PullSecret: installConfig.Config.PullSecret,
104+
RawNMStateConfig: imageBasedConfig.Config.NetworkConfig.String(),
105+
ReleaseRegistry: imageBasedConfig.Config.ReleaseRegistry,
106+
SSHKey: installConfig.Config.SSHKey,
107+
}
108+
109+
if len(imageBasedConfig.Config.AdditionalNTPSources) > 0 {
110+
cc.Config.ChronyConfig = chronyConfWithAdditionalNTPSources(imageBasedConfig.Config.AdditionalNTPSources)
111+
}
112+
113+
if installConfig.Config.AdditionalTrustBundle != "" {
114+
cc.Config.AdditionalTrustBundle = imagebased.AdditionalTrustBundle{
115+
UserCaBundle: installConfig.Config.AdditionalTrustBundle,
116+
}
117+
118+
if installConfig.Config.AdditionalTrustBundlePolicy == types.PolicyAlways ||
119+
(installConfig.Config.AdditionalTrustBundlePolicy == types.PolicyProxyOnly && installConfig.Config.Proxy != nil) {
120+
cc.Config.AdditionalTrustBundle.ProxyConfigmapName = userCABundleConfigMapName
121+
cc.Config.AdditionalTrustBundle.ProxyConfigmapBundle = installConfig.Config.AdditionalTrustBundle
122+
}
123+
}
124+
125+
cc.Config.KubeconfigCryptoRetention = imagebased.KubeConfigCryptoRetention{
126+
KubeAPICrypto: imagebased.KubeAPICrypto{
127+
ServingCrypto: imagebased.ServingCrypto{
128+
LoadbalancerSignerPrivateKey: string(serverLBSignerCertKey.Key()),
129+
LocalhostSignerPrivateKey: string(serverLocalhostSignerCertKey.Key()),
130+
ServiceNetworkSignerPrivateKey: string(serverServiceNetworkSignerCertKey.Key()),
131+
},
132+
ClientAuthCrypto: imagebased.ClientAuthCrypto{
133+
AdminCACertificate: string(adminKubeConfigSignerCertKey.Cert()),
134+
},
135+
},
136+
IngresssCrypto: imagebased.IngresssCrypto{
137+
IngressCA: string(ingressOperatorSignerCertKey.Key()),
138+
},
139+
}
140+
141+
// validation for the length of the MachineNetwork is performed in the InstallConfig
142+
cc.Config.MachineNetwork = installConfig.Config.Networking.MachineNetwork[0].CIDR.String()
143+
144+
clusterConfigurationData, err := json.Marshal(cc.Config)
145+
if err != nil {
146+
return fmt.Errorf("failed to marshal image-based installer ClusterConfiguration: %w", err)
147+
}
148+
149+
cc.File = &asset.File{
150+
Filename: clusterConfigurationFilename,
151+
Data: clusterConfigurationData,
152+
}
153+
154+
return cc.finish()
155+
}
156+
157+
// Files returns the files generated by the asset.
158+
func (cc *ClusterConfiguration) Files() []*asset.File {
159+
if cc.File != nil {
160+
return []*asset.File{cc.File}
161+
}
162+
return []*asset.File{}
163+
}
164+
165+
// Load returns ClusterConfiguration asset from the disk.
166+
func (cc *ClusterConfiguration) Load(f asset.FileFetcher) (bool, error) {
167+
file, err := f.FetchByName(clusterConfigurationFilename)
168+
if err != nil {
169+
if os.IsNotExist(err) {
170+
return false, nil
171+
}
172+
return false, fmt.Errorf("failed to load %s file: %w", clusterConfigurationFilename, err)
173+
}
174+
175+
config := &imagebased.SeedReconfiguration{}
176+
strErrs, err := k8sjson.UnmarshalStrict(file.Data, config)
177+
if len(strErrs) > 0 {
178+
return false, fmt.Errorf("failed to unmarshal %s: %w", clusterConfigurationFilename, errors.Join(strErrs...))
179+
}
180+
if err != nil {
181+
return false, fmt.Errorf("failed to unmarshal %s: invalid JSON syntax", clusterConfigurationFilename)
182+
}
183+
184+
cc.File, cc.Config = file, config
185+
if err = cc.finish(); err != nil {
186+
return false, err
187+
}
188+
189+
return true, nil
190+
}
191+
192+
func (cc *ClusterConfiguration) finish() error {
193+
if cc.Config == nil {
194+
return errors.New("missing configuration or manifest file")
195+
}
196+
return nil
197+
}
198+
199+
func chronyConfWithAdditionalNTPSources(sources []string) string {
200+
content := defaultChronyConf[:]
201+
for _, source := range sources {
202+
content += fmt.Sprintf("\nserver %s iburst", source)
203+
}
204+
return content
205+
}

0 commit comments

Comments
 (0)