option.go 3.32 KB
Newer Older
songlinfeng's avatar
songlinfeng committed
1
2
3
4
5
6
7
8
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
/**
# Copyright (c) 2024, HCUOpt CORPORATION.  All rights reserved.
**/

package containerd

import (
	"dtk-container-toolkit/internal/logger"
	"dtk-container-toolkit/pkg/config/engine"
	"fmt"
	"os"

	"github.com/pelletier/go-toml"
)

const (
	defaultRuntimeType = "io.containerd.runc.v2"
)

type builder struct {
	logger               logger.Interface
	path                 string
	runtimeType          string
	useLegacyConfig      bool
	containerAnnotations []string
}

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

// WithLogger sets the logger for the config builder
func WithLogger(logger logger.Interface) Option {
	return func(b *builder) {
		b.logger = logger
	}
}

// WithPath sets the path for the config builder
func WithPath(path string) Option {
	return func(b *builder) {
		b.path = path
	}
}

// WithRuntimeType sets the runtime type for the config builder
func WithRuntimeType(runtimeType string) Option {
	return func(b *builder) {
		b.runtimeType = runtimeType
	}
}

// WithUseLegacyConfig sets the useLegacyConfig flag for the config builder
func WithUseLegacyConfig(useLegacyConfig bool) Option {
	return func(b *builder) {
		b.useLegacyConfig = useLegacyConfig
	}
}

// WithContainerAnnotations sets the container annotations for the config builder
func WithContainerAnnotations(containerAnnotations ...string) Option {
	return func(b *builder) {
		b.containerAnnotations = containerAnnotations
	}
}

func (b *builder) build() (engine.Interface, error) {
	if b.path == "" {
		return nil, fmt.Errorf("config path is empty")
	}

	if b.runtimeType == "" {
		b.runtimeType = defaultRuntimeType
	}

	config, err := b.loadConfig(b.path)
	if err != nil {
		return nil, fmt.Errorf("failed to load config: %v", err)
	}
	config.RuntimeType = b.runtimeType
	config.UseDefaultRuntimeName = !b.useLegacyConfig
	config.ContainerAnnotations = b.containerAnnotations

	version, err := config.parseVersion(b.useLegacyConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to parse config version: %v", err)
	}
	switch version {
	case 1:
		return (*ConfigV1)(config), nil
	case 2:
		return config, nil
	}

	return nil, fmt.Errorf("unsupported config version: %v", version)
}

// loadConfig loads the containerd config from disk
func (b *builder) loadConfig(config string) (*Config, error) {
	info, err := os.Stat(config)
	if os.IsExist(err) && info.IsDir() {
		return nil, fmt.Errorf("config file is a directory")
	}

	if os.IsNotExist(err) {
		b.logger.Infof("Config file does not exist; using empty config")
		config = "/dev/null"
	} else {
		b.logger.Infof("Loading config from %v", config)
	}

	tomlConfig, err := toml.LoadFile(config)
	if err != nil {
		return nil, err
	}

	cfg := Config{
		Tree: tomlConfig,
	}
	return &cfg, nil
}

// parseVersion returns the version of the config
func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
	defaultVersion := 2
	if useLegacyConfig {
		defaultVersion = 1
	}

	switch v := c.Get("version").(type) {
	case nil:
		switch len(c.Keys()) {
		case 0: // No config exists, or the config file is empty, use version inferred from containerd
			return defaultVersion, nil
		default: // A config file exists, has content, and no version is set
			return 1, nil
		}
	case int64:
		return int(v), nil
	default:
		return -1, fmt.Errorf("unsupported type for version field: %v", v)
	}
}