-
Notifications
You must be signed in to change notification settings - Fork 10
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
Generates Containerd and crio CRI configs #59
Changes from 25 commits
b0304ff
8a5ba11
c291e6a
fe299f8
b6b5dea
91222d4
09419ce
c60f7a4
18b5633
dec1ba0
ef0d82a
74fc4b9
3d0e209
6e54a14
8904e99
d14af7e
7254c1b
d111454
222c666
d2180bf
97f8b01
949c60c
1b6b45c
8624668
771a6e3
a08405d
0dc1160
f754085
abb1a3e
a641df3
2f11137
17f8ffa
c194bb0
86430aa
a656219
1e8dc43
5ca08c7
e578bd4
7062d10
9c6d293
9782e7e
fc24959
2fdb90b
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 |
---|---|---|
|
@@ -29,3 +29,7 @@ dist/ | |
zot/cache.db | ||
secrets.txt | ||
__debug_bin1949266242 | ||
|
||
/zot | ||
/runtime | ||
runtime/containerd/config.toml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package runtime | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"container-registry.com/harbor-satellite/internal/config" | ||
"container-registry.com/harbor-satellite/internal/utils" | ||
"container-registry.com/harbor-satellite/logger" | ||
"container-registry.com/harbor-satellite/registry" | ||
toml "github.com/pelletier/go-toml" | ||
"github.com/rs/zerolog" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
const ( | ||
ContainerDCertPath = "/etc/containerd/certs.d" | ||
DefaultGeneratedTomlName = "config.toml" | ||
ContainerdRuntime = "containerd" | ||
DefaultContainerdConfigPath = "/etc/containerd/config.toml" | ||
DefaultConfigVersion = 2 | ||
) | ||
|
||
type ContainerdController interface { | ||
Load(ctx context.Context, log *zerolog.Logger) (*registry.DefaultZotConfig, error) | ||
Generate(ctx context.Context, configPath string, log *zerolog.Logger) error | ||
} | ||
|
||
var DefaultGenPath string | ||
|
||
func init() { | ||
cwd, err := os.Getwd() | ||
if err != nil { | ||
fmt.Printf("Error getting current working directory: %v\n", err) | ||
if _, err := os.Stat(DefaultGenPath); os.IsNotExist(err) { | ||
err := os.MkdirAll(DefaultGenPath, os.ModePerm) | ||
if err != nil { | ||
fmt.Printf("Error creating default directory: %v\n", err) | ||
} | ||
} | ||
} else { | ||
DefaultGenPath = filepath.Join(cwd, "runtime/containerd") | ||
} | ||
} | ||
|
||
func NewContainerdCommand() *cobra.Command { | ||
var generateConfig bool | ||
var defaultZotConfig *registry.DefaultZotConfig | ||
var containerdConfigPath string | ||
var containerDCertPath string | ||
|
||
containerdCmd := &cobra.Command{ | ||
Use: "containerd", | ||
Short: "Creates the config file for the containerd runtime to fetch the images from the local repository", | ||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { | ||
var err error | ||
utils.SetupContextForCommand(cmd) | ||
config.InitConfig() | ||
log := logger.FromContext(cmd.Context()) | ||
if config.GetOwnRegistry() { | ||
log.Info().Msg("Using own registry for config generation") | ||
address, err := utils.ValidateRegistryAddress(config.GetOwnRegistryAdr(), config.GetOwnRegistryPort()) | ||
if err != nil { | ||
log.Err(err).Msg("Error validating registry address") | ||
return err | ||
} | ||
log.Info().Msgf("Registry address validated: %s", address) | ||
defaultZotConfig.HTTP.Address = config.GetOwnRegistryAdr() | ||
defaultZotConfig.HTTP.Port = config.GetOwnRegistryPort() | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
log.Info().Msg("Using default registry for config generation") | ||
defaultZotConfig, err = registry.ReadConfig(config.GetZotConfigPath()) | ||
if err != nil || defaultZotConfig == nil { | ||
return fmt.Errorf("could not read config: %w", err) | ||
} | ||
log.Info().Msgf("Default config read successfully: %v", defaultZotConfig.HTTP.Address+":"+defaultZotConfig.HTTP.Port) | ||
} | ||
return utils.CreateRuntimeDirectory(DefaultGenPath) | ||
}, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
log := logger.FromContext(cmd.Context()) | ||
sourceRegistry := config.GetRemoteRegistryURL() | ||
satelliteHostConfig := NewSatelliteHostConfig(defaultZotConfig.GetLocalRegistryURL(), sourceRegistry) | ||
if generateConfig { | ||
log.Info().Msg("Generating containerd config file for containerd ...") | ||
log.Info().Msgf("Fetching containerd config from path path: %s", containerdConfigPath) | ||
err := GenerateContainerdHostConfig(containerDCertPath, DefaultGenPath, log, *satelliteHostConfig) | ||
if err != nil { | ||
log.Err(err).Msg("Error generating containerd config") | ||
return fmt.Errorf("could not generate containerd config: %w", err) | ||
} | ||
return GenerateConfig(defaultZotConfig, log, containerdConfigPath, containerDCertPath) | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
containerdCmd.Flags().BoolVarP(&generateConfig, "gen", "g", false, "Generate the containerd config file") | ||
containerdCmd.PersistentFlags().StringVarP(&containerdConfigPath, "path", "p", DefaultContainerdConfigPath, "Path to the containerd config file of the container runtime") | ||
containerdCmd.PersistentFlags().StringVarP(&containerDCertPath, "cert-path", "c", ContainerDCertPath, "Path to the containerd cert directory") | ||
containerdCmd.AddCommand(NewReadConfigCommand(ContainerdRuntime)) | ||
return containerdCmd | ||
} | ||
|
||
// GenerateConfig generates the containerd config file for the containerd runtime | ||
// It takes the zot config a logger and the containerd config path | ||
// It reads the containerd config file and adds the local registry to the config file | ||
func GenerateConfig(defaultZotConfig *registry.DefaultZotConfig, log *zerolog.Logger, containerdConfigPath, containerdCertPath string) error { | ||
// First Read the present config file at the configPath | ||
data, err := utils.ReadFile(containerdConfigPath, false) | ||
if err != nil { | ||
log.Err(err).Msg("Error reading config file") | ||
return fmt.Errorf("could not read config file: %w", err) | ||
} | ||
// Now we marshal the data into the containerd config | ||
containerdConfig := &ContainerdConfigToml{} | ||
err = toml.Unmarshal(data, containerdConfig) | ||
if err != nil { | ||
log.Err(err).Msg("Error unmarshalling config") | ||
return fmt.Errorf("could not unmarshal config: %w", err) | ||
} | ||
// Add the certs.d path to the config | ||
if containerdConfig.Plugins.Cri.Registry.ConfigPath == "" { | ||
containerdConfig.Plugins.Cri.Registry.ConfigPath = containerdCertPath | ||
} | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Set default version | ||
if containerdConfig.Version == 0 { | ||
containerdConfig.Version = DefaultConfigVersion | ||
} | ||
// if config disabled plugins container cri then remove it | ||
if len(containerdConfig.DisabledPlugins) > 0 { | ||
filteredPlugins := make([]string, 0, len(containerdConfig.DisabledPlugins)) | ||
for _, plugin := range containerdConfig.DisabledPlugins { | ||
if plugin != "cri" { | ||
filteredPlugins = append(filteredPlugins, plugin) | ||
} | ||
} | ||
containerdConfig.DisabledPlugins = filteredPlugins | ||
} | ||
// ToDo: Find a way to remove the unwanted configuration added to the config file while marshalling | ||
pathToWrite := filepath.Join(DefaultGenPath, DefaultGeneratedTomlName) | ||
log.Info().Msgf("Writing the containerd config to path: %s", pathToWrite) | ||
// Now we write the config to the file | ||
data, err = toml.Marshal(containerdConfig) | ||
dataStr := string(data) | ||
dataStr = strings.Replace(dataStr, "[plugins]\n", "", 1) | ||
data = []byte(dataStr) | ||
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. Replace manual string manipulation with structured TOML handling. The current approach of manually removing "[plugins]\n" through string manipulation is fragile and could break with different TOML formatters. Consider using the TOML library's features to handle the structure properly: -// ToDo: Find a way to remove the unwanted configuration added to the config file while marshalling
-pathToWrite := filepath.Join(DefaultGenPath, DefaultGeneratedTomlName)
-log.Info().Msgf("Writing the containerd config to path: %s", pathToWrite)
-// Now we write the config to the file
-data, err = toml.Marshal(containerdConfig)
-dataStr := string(data)
-dataStr = strings.Replace(dataStr, "[plugins]\n", "", 1)
-data = []byte(dataStr)
+pathToWrite := filepath.Join(DefaultGenPath, DefaultGeneratedTomlName)
+log.Info().Msgf("Writing the containerd config to path: %s", pathToWrite)
+
+// Create a clean structure without the plugins section
+cleanConfig := &ContainerdConfigToml{
+ Version: containerdConfig.Version,
+ DisabledPlugins: containerdConfig.DisabledPlugins,
+ Plugins: containerdConfig.Plugins,
+}
+data, err = toml.Marshal(cleanConfig)
|
||
if err != nil { | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
log.Err(err).Msg("Error marshalling config") | ||
return fmt.Errorf("could not marshal config: %w", err) | ||
} | ||
err = utils.WriteFile(pathToWrite, data) | ||
if err != nil { | ||
log.Err(err).Msg("Error writing config to file") | ||
return fmt.Errorf("could not write config to file: %w", err) | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
package runtime | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// ContainerdConfigToml provides containerd configuration data for the server | ||
type ContainerdConfigToml struct { | ||
// Version of the config file | ||
Version int `toml:"version,omitempty"` | ||
// Root is the path to a directory where containerd will store persistent data | ||
Root string `toml:"root,omitempty"` | ||
// State is the path to a directory where containerd will store transient data | ||
State string `toml:"state,omitempty"` | ||
// TempDir is the path to a directory where to place containerd temporary files | ||
TempDir string `toml:"temp,omitempty"` | ||
// PluginDir is the directory for dynamic plugins to be stored | ||
// | ||
// Deprecated: Please use proxy or binary external plugins. | ||
PluginDir string `toml:"plugin_dir,omitempty"` | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// GRPC configuration settings | ||
GRPC GRPCConfig `toml:"grpc,omitempty"` | ||
// TTRPC configuration settings | ||
TTRPC TTRPCConfig `toml:"ttrpc,omitempty"` | ||
// Debug and profiling settings | ||
Debug Debug `toml:"debug,omitempty"` | ||
// Metrics and monitoring settings | ||
Metrics MetricsConfig `toml:"metrics,omitempty"` | ||
// DisabledPlugins are IDs of plugins to disable. Disabled plugins won't be | ||
// initialized and started. | ||
// DisabledPlugins must use a fully qualified plugin URI. | ||
DisabledPlugins []string `toml:"disabled_plugins,omitempty"` | ||
// RequiredPlugins are IDs of required plugins. Containerd exits if any | ||
// required plugin doesn't exist or fails to be initialized or started. | ||
// RequiredPlugins must use a fully qualified plugin URI. | ||
RequiredPlugins []string `toml:"required_plugins,omitempty"` | ||
// Plugins provides plugin specific configuration for the initialization of a plugin | ||
Plugins PluginsConfig `toml:"plugins,omitempty"` | ||
// OOMScore adjust the containerd's oom score | ||
OOMScore int `toml:"oom_score,omitempty"` | ||
// Cgroup specifies cgroup information for the containerd daemon process | ||
Cgroup CgroupConfig `toml:"cgroup,omitempty"` | ||
// ProxyPlugins configures plugins which are communicated to over GRPC | ||
ProxyPlugins map[string]ProxyPlugin `toml:"proxy_plugins,omitempty"` | ||
// Timeouts specified as a duration | ||
Timeouts map[string]string `toml:"timeouts,omitempty"` | ||
// Imports are additional file path list to config files that can overwrite main config file fields | ||
Imports []string `toml:"imports,omitempty"` | ||
// StreamProcessors configuration | ||
StreamProcessors map[string]StreamProcessor `toml:"stream_processors,omitempty"` | ||
} | ||
|
||
type StreamProcessor struct { | ||
// Accepts specific media-types | ||
Accepts []string `toml:"accepts,omitempty"` | ||
// Returns the media-type | ||
Returns string `toml:"returns,omitempty"` | ||
// Path or name of the binary | ||
Path string `toml:"path"` | ||
// Args to the binary | ||
Args []string `toml:"args,omitempty"` | ||
// Environment variables for the binary | ||
Env []string `toml:"env,omitempty"` | ||
} | ||
|
||
type GRPCConfig struct { | ||
Address string `toml:"address"` | ||
TCPAddress string `toml:"tcp_address,omitempty"` | ||
TCPTLSCA string `toml:"tcp_tls_ca,omitempty"` | ||
TCPTLSCert string `toml:"tcp_tls_cert,omitempty"` | ||
TCPTLSKey string `toml:"tcp_tls_key,omitempty"` | ||
UID int `toml:"uid,omitempty"` | ||
GID int `toml:"gid,omitempty"` | ||
MaxRecvMsgSize int `toml:"max_recv_message_size,omitempty"` | ||
MaxSendMsgSize int `toml:"max_send_message_size,omitempty"` | ||
} | ||
|
||
// TTRPCConfig provides TTRPC configuration for the socket | ||
type TTRPCConfig struct { | ||
Address string `toml:"address"` | ||
UID int `toml:"uid,omitempty"` | ||
GID int `toml:"gid,omitempty"` | ||
} | ||
|
||
// Debug provides debug configuration | ||
type Debug struct { | ||
Address string `toml:"address,omitempty"` | ||
UID int `toml:"uid,omitempty"` | ||
GID int `toml:"gid,omitempty"` | ||
Level string `toml:"level,omitempty"` | ||
// Format represents the logging format. Supported values are 'text' and 'json'. | ||
Format string `toml:"format,omitempty"` | ||
} | ||
|
||
// MetricsConfig provides metrics configuration | ||
type MetricsConfig struct { | ||
Address string `toml:"address,omitempty"` | ||
GRPCHistogram bool `toml:"grpc_histogram,omitempty"` | ||
} | ||
|
||
// CgroupConfig provides cgroup configuration | ||
type CgroupConfig struct { | ||
Path string `toml:"path,omitempty"` | ||
} | ||
|
||
// ProxyPlugin provides a proxy plugin configuration | ||
type ProxyPlugin struct { | ||
Type string `toml:"type"` | ||
Address string `toml:"address"` | ||
Platform string `toml:"platform,omitempty"` | ||
Exports map[string]string `toml:"exports,omitempty"` | ||
Capabilities []string `toml:"capabilities,omitempty"` | ||
} | ||
|
||
type PluginsConfig struct { | ||
Cri CriConfig `toml:"io.containerd.grpc.v1.cri,omitempty"` | ||
Cgroups MonitorConfig `toml:"io.containerd.monitor.v1.cgroups,omitempty"` | ||
LinuxRuntime interface{} `toml:"io.containerd.runtime.v1.linux,omitempty"` | ||
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. Specify the type for Using Apply this diff to define a concrete type: -LinuxRuntime interface{} `toml:"io.containerd.runtime.v1.linux,omitempty"`
+LinuxRuntime LinuxRuntimeConfig `toml:"io.containerd.runtime.v1.linux,omitempty"` And define the type LinuxRuntimeConfig struct {
// Add relevant fields here
} |
||
Scheduler GCSchedulerConfig `toml:"io.containerd.gc.v1.scheduler,omitempty"` | ||
Bolt interface{} `toml:"io.containerd.metadata.v1.bolt,omitempty"` | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Task RuntimeV2TaskConfig `toml:"io.containerd.runtime.v2.task,omitempty"` | ||
Opt interface{} `toml:"io.containerd.internal.v1.opt,omitempty"` | ||
Restart interface{} `toml:"io.containerd.internal.v1.restart,omitempty"` | ||
Tracing interface{} `toml:"io.containerd.internal.v1.tracing,omitempty"` | ||
Otlp interface{} `toml:"io.containerd.tracing.processor.v1.otlp,omitempty"` | ||
Aufs interface{} `toml:"io.containerd.snapshotter.v1.aufs,omitempty"` | ||
Btrfs interface{} `toml:"io.containerd.snapshotter.v1.btrfs,omitempty"` | ||
Devmapper interface{} `toml:"io.containerd.snapshotter.v1.devmapper,omitempty"` | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Native interface{} `toml:"io.containerd.snapshotter.v1.native,omitempty"` | ||
Overlayfs interface{} `toml:"io.containerd.snapshotter.v1.overlayfs,omitempty"` | ||
Zfs interface{} `toml:"io.containerd.snapshotter.v1.zfs,omitempty"` | ||
} | ||
|
||
type MonitorConfig struct { | ||
NoPrometheus bool `toml:"no_prometheus,omitempty"` | ||
} | ||
|
||
type GCSchedulerConfig struct { | ||
PauseThreshold float64 `toml:"pause_threshold,omitempty"` | ||
DeletionThreshold int `toml:"deletion_threshold,omitempty"` | ||
MutationThreshold int `toml:"mutation_threshold,omitempty"` | ||
ScheduleDelay string `toml:"schedule_delay,omitempty"` | ||
StartupDelay string `toml:"startup_delay,omitempty"` | ||
} | ||
|
||
type RuntimeV2TaskConfig struct { | ||
Platforms []string `toml:"platforms,omitempty"` | ||
SchedCore bool `toml:"sched_core,omitempty"` | ||
} | ||
|
||
type CriConfig struct { | ||
Containerd CriContainerdConfig `toml:"containerd,omitempty"` | ||
Registry RegistryConfig `toml:"registry,omitempty"` | ||
} | ||
|
||
type CriContainerdConfig struct { | ||
DefaultRuntimeName string `toml:"default_runtime_name,omitempty"` | ||
Runtimes map[string]RuntimeConfig `toml:"runtimes,omitempty"` | ||
} | ||
|
||
type RuntimeConfig struct { | ||
PrivilegedWithoutHostDevices bool `toml:"privileged_without_host_devices,omitempty"` | ||
RuntimeType string `toml:"runtime_type"` | ||
Options RuntimeOptions `toml:"options,omitempty"` | ||
} | ||
|
||
type RuntimeOptions struct { | ||
BinaryName string `toml:"BinaryName,omitempty"` | ||
} | ||
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
type RegistryConfig struct { | ||
ConfigPath string `toml:"config_path,omitempty"` | ||
} |
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.
Fix initialization bug in error handling path.
When
os.Getwd()
fails,DefaultGenPath
is used inos.Stat()
before being initialized. This will cause a runtime error.func init() { cwd, err := os.Getwd() if err != nil { fmt.Printf("Error getting current working directory: %v\n", err) + DefaultGenPath = "/runtime/containerd" // Set fallback path first if _, err := os.Stat(DefaultGenPath); os.IsNotExist(err) { err := os.MkdirAll(DefaultGenPath, os.ModePerm) if err != nil { fmt.Printf("Error creating default directory: %v\n", err) } } } else { DefaultGenPath = filepath.Join(cwd, "runtime/containerd") } }
📝 Committable suggestion