-
Notifications
You must be signed in to change notification settings - Fork 42
feat(): layer2 support for CAPP #787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
05cbc0c
15aaba7
ebfddac
1d2b71e
e1a19b5
cdf7155
4cfb7b8
f762e5b
a452289
5f33a86
aad4223
25510bd
16fdf8d
b7e1120
fcfcaac
b7e871a
ddf37d9
d901fe6
c55ea3f
4c64c5d
d4d4b25
4d30dcc
4f9c8fc
4efbfd8
07516df
6207973
7e68faf
475365c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,4 @@ spec: | |
spec: | ||
containers: | ||
- name: manager | ||
imagePullPolicy: IfNotPresent | ||
imagePullPolicy: Always | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. local testing only |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package layer2 | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
const ( | ||
cloudConfigHeader = "#cloud-config" | ||
jinjaHeader = "## template: jinja" | ||
) | ||
|
||
// CloudConfigMerger handles the merging of cloud configs | ||
type CloudConfigMerger struct { | ||
} | ||
|
||
// NewCloudConfigMerger creates a new instance of CloudConfigMerger | ||
func NewCloudConfigMerger() *CloudConfigMerger { | ||
return &CloudConfigMerger{} | ||
} | ||
|
||
// configHeaders represents the headers found in a cloud-config file | ||
type configHeaders struct { | ||
hasJinja bool | ||
hasCloudConfig bool | ||
} | ||
|
||
// stripHeaders removes the template and cloud-config headers and returns the remaining content | ||
func stripHeaders(data string) (string, configHeaders) { | ||
headers := configHeaders{} | ||
lines := strings.Split(strings.TrimSpace(data), "\n") | ||
startIndex := 0 | ||
|
||
for i, line := range lines { | ||
trimmedLine := strings.TrimSpace(line) | ||
switch trimmedLine { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this assumes there will not be any other header ! Can we modify this to be more generic - is there a way to identify headers apart from string equals |
||
case jinjaHeader: | ||
headers.hasJinja = true | ||
startIndex = i + 1 | ||
case cloudConfigHeader: | ||
headers.hasCloudConfig = true | ||
startIndex = i + 1 | ||
default: | ||
if trimmedLine != "" && !strings.HasPrefix(trimmedLine, "#") { | ||
return strings.Join(lines[startIndex:], "\n"), headers | ||
} | ||
} | ||
} | ||
return "", headers | ||
} | ||
|
||
// deepMerge recursively merges two maps | ||
func (m *CloudConfigMerger) deepMerge(base, overlay map[string]interface{}) map[string]interface{} { | ||
result := make(map[string]interface{}) | ||
|
||
// Copy base map | ||
for k, v := range base { | ||
result[k] = v | ||
} | ||
|
||
// Merge overlay | ||
for k, v := range overlay { | ||
if baseVal, exists := result[k]; exists { | ||
// If both values are maps, merge them recursively | ||
if baseMap, ok := baseVal.(map[string]interface{}); ok { | ||
if overlayMap, ok := v.(map[string]interface{}); ok { | ||
result[k] = m.deepMerge(baseMap, overlayMap) | ||
continue | ||
} | ||
} | ||
|
||
// If both values are slices, append them | ||
if baseSlice, ok := baseVal.([]interface{}); ok { | ||
if overlaySlice, ok := v.([]interface{}); ok { | ||
result[k] = append(baseSlice, overlaySlice...) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// For all other cases, overlay value takes precedence | ||
result[k] = v | ||
} | ||
|
||
return result | ||
} | ||
|
||
// buildHeader constructs the appropriate header based on the input configurations | ||
func buildHeader(bootstrapHeaders, layer2Headers configHeaders) string { | ||
var headers []string | ||
|
||
// If either input has the Jinja header, include it in the output | ||
if bootstrapHeaders.hasJinja || layer2Headers.hasJinja { | ||
headers = append(headers, jinjaHeader) | ||
} | ||
|
||
// Always include the cloud-config header | ||
headers = append(headers, cloudConfigHeader) | ||
|
||
return strings.Join(headers, "\n") | ||
} | ||
|
||
// MergeConfigs combines bootstrap data with layer2 config | ||
func (m *CloudConfigMerger) MergeConfigs(bootstrapData string, layer2UserData string) (string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we make this generic as well ? CloudConfigMerger.WithBootstrapData(bdata).
WithLayer2Data(l2data).
WithAddlUserDatas([]userdatas). // this can be optional and can be extensible
MergeConfigs() |
||
// Strip headers and get header info | ||
bootstrapStripped, bootstrapHeaders := stripHeaders(bootstrapData) | ||
layer2Stripped, layer2Headers := stripHeaders(layer2UserData) | ||
|
||
// Validate that at least one input has the cloud-config header | ||
if !bootstrapHeaders.hasCloudConfig && !layer2Headers.hasCloudConfig { | ||
return "", fmt.Errorf("neither input contains #cloud-config header") | ||
} | ||
|
||
var bootstrapConfig, layer2UserDataConfig map[string]interface{} | ||
|
||
if bootstrapStripped != "" { | ||
if err := yaml.Unmarshal([]byte(bootstrapStripped), &bootstrapConfig); err != nil { | ||
return "", fmt.Errorf("error parsing bootstrap YAML: %v", err) | ||
} | ||
} else { | ||
bootstrapConfig = make(map[string]interface{}) | ||
} | ||
|
||
if layer2Stripped != "" { | ||
if err := yaml.Unmarshal([]byte(layer2Stripped), &layer2UserDataConfig); err != nil { | ||
return "", fmt.Errorf("error parsing layer2 YAML: %v", err) | ||
} | ||
} else { | ||
layer2UserDataConfig = make(map[string]interface{}) | ||
} | ||
Comment on lines
+124
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bootstrapConfig := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(layer2Stripped), &layer2UserDataConfig); err != nil {
return "", fmt.Errorf("error parsing layer2 YAML: %v", err)
}
layer2UserDataConfig := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(layer2Stripped), &layer2UserDataConfig); err != nil {
return "", fmt.Errorf("error parsing layer2 YAML: %v", err)
} |
||
|
||
// Merge configurations | ||
mergedConfig := m.deepMerge(layer2UserDataConfig, bootstrapConfig) | ||
|
||
// Convert merged config back to YAML | ||
result, err := yaml.Marshal(mergedConfig) | ||
if err != nil { | ||
return "", fmt.Errorf("error marshaling merged config: %v", err) | ||
} | ||
|
||
// Build appropriate headers and combine with content | ||
header := buildHeader(bootstrapHeaders, layer2Headers) | ||
return fmt.Sprintf("%s\n%s", header, string(result)), nil | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep this for your local testing only