builder.go 4.02 KB
Newer Older
songlinfeng's avatar
songlinfeng committed
1
2
3
4
5
6
7
/**
# Copyright (c) 2024, HCUOpt CORPORATION.  All rights reserved.
**/

package spec

import (
8
	"dcu-container-toolkit/pkg/c3000cdi/transform"
songlinfeng's avatar
songlinfeng committed
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
	"fmt"
	"os"

	"tags.cncf.io/container-device-interface/pkg/cdi"
	"tags.cncf.io/container-device-interface/pkg/parser"
	"tags.cncf.io/container-device-interface/specs-go"
)

type builder struct {
	raw         *specs.Spec
	version     string
	vendor      string
	class       string
	deviceSpecs []specs.Device
	edits       specs.ContainerEdits
	format      string

	mergedDeviceOptions []transform.MergedDeviceOption
	noSimplify          bool
	permissions         os.FileMode

	transformOnSave transform.Transformer
}

// newBuilder creates a new spec builder with the supplied options
func newBuilder(opts ...Option) *builder {
	s := &builder{}
	for _, opt := range opts {
		opt(s)
	}

	if s.raw != nil {
		s.noSimplify = true
		vendor, class := parser.ParseQualifier(s.raw.Kind)
		if s.vendor == "" {
			s.vendor = vendor
		}
		if s.class == "" {
			s.class = class
		}
		if s.version == "" {
			s.version = s.raw.Version
		}
	}
	if s.version == "" {
		s.transformOnSave = &setMinimumRequiredVersion{}
		s.version = cdi.CurrentVersion
	}
	if s.vendor == "" {
		s.vendor = "c-3000.com"
	}
	if s.class == "" {
		s.class = "hcu"
	}
	if s.format == "" {
		s.format = FormatYAML
	}
	if s.permissions == 0 {
		s.permissions = 0644
	}
	return s
}

// Build builds a CDI spec form the spec builder.
func (o *builder) Build() (*spec, error) {
	raw := o.raw
	if raw == nil {
		raw = &specs.Spec{
			Version:        o.version,
			Kind:           fmt.Sprintf("%s/%s", o.vendor, o.class),
			Devices:        o.deviceSpecs,
			ContainerEdits: o.edits,
		}
	}
	if raw.Version == "" {
		raw.Version = o.version
	}

	if !o.noSimplify {
		err := transform.NewSimplifier().Transform(raw)
		if err != nil {
			return nil, fmt.Errorf("failed to simplify spec: %v", err)
		}
	}

	if len(o.mergedDeviceOptions) > 0 {
		merge, err := transform.NewMergedDevice(o.mergedDeviceOptions...)
		if err != nil {
			return nil, fmt.Errorf("failed to create merged device transformer: %v", err)
		}
		if err := merge.Transform(raw); err != nil {
			return nil, fmt.Errorf("failed to merge devices: %v", err)
		}
	}

	s := spec{
		Spec:            raw,
		format:          o.format,
		permissions:     o.permissions,
		transformOnSave: o.transformOnSave,
	}
	return &s, nil
}

// Option defines a function that can be used to configure the spec builder.
type Option func(*builder)

// WithDeviceSpecs sets the device specs for the spec builder
func WithDeviceSpecs(deviceSpecs []specs.Device) Option {
	return func(o *builder) {
		o.deviceSpecs = deviceSpecs
	}
}

// WithEdits sets the container edits for the spec builder
func WithEdits(edits specs.ContainerEdits) Option {
	return func(o *builder) {
		o.edits = edits
	}
}

// WithVersion sets the version for the spec builder
func WithVersion(version string) Option {
	return func(o *builder) {
		o.version = version
	}
}

// WithVendor sets the vendor for the spec builder
func WithVendor(vendor string) Option {
	return func(o *builder) {
		o.vendor = vendor
	}
}

// WithClass sets the class for the spec builder
func WithClass(class string) Option {
	return func(o *builder) {
		o.class = class
	}
}

// WithFormat sets the output file format
func WithFormat(format string) Option {
	return func(o *builder) {
		o.format = format
	}
}

// WithNoSimplify sets whether the spec must be simplified
func WithNoSimplify(noSimplify bool) Option {
	return func(o *builder) {
		o.noSimplify = noSimplify
	}
}

// WithRawSpec sets the raw spec for the spec builder
func WithRawSpec(raw *specs.Spec) Option {
	return func(o *builder) {
		o.raw = raw
	}
}

// WithPermissions sets the permissions for the generated spec file
func WithPermissions(permissions os.FileMode) Option {
	return func(o *builder) {
		o.permissions = permissions
	}
}

// WithMergedDeviceOptions sets the options for generating a merged device.
func WithMergedDeviceOptions(opts ...transform.MergedDeviceOption) Option {
	return func(o *builder) {
		o.mergedDeviceOptions = opts
	}
}