/
githubmirror
/
ignition
Обзор
Документация
Войти
/
githubmirror
/
ignition
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
internal/providers/cloudstack/cloudstack.go
253 строки
6 KB
Steven Presti
*: update to v3_7_experimental spec
11 фев 2026, 23:55
11 фев 2026, 23:55
3252aa5
Код
Авторство
О чём код?
// Copyright 2017 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // The CloudStack provider fetches configurations from the userdata available in // the config-drive. // NOTE: This provider is still EXPERIMENTAL. package cloudstack import ( "bufio" "context" "fmt" "net" "net/url" "os" "os/exec" "path/filepath" "strings" "time" "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" ut "github.com/coreos/ignition/v2/internal/util" "github.com/coreos/vcontext/report" ) const ( configDriveUserdataPath = "/cloudstack/userdata/user_data.txt" LeaseRetryInterval = 500 * time.Millisecond ) func init() { platform.Register(platform.Provider{ Name: "cloudstack", Fetch: fetchConfig, }) } func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { // The fetch-offline approach doesn't work well here because of the "split // personality" of this provider. See: // https://github.com/coreos/ignition/issues/1081 if f.Offline { return types.Config{}, report.Report{}, resource.ErrNeedNet } var data []byte errChan := make(chan error) ctx, cancel := context.WithCancel(context.Background()) dispatchCount := 0 dispatch := func(name string, fn func() ([]byte, error)) { dispatchCount++ go func() { raw, err := fn() if err != nil { switch err { case context.Canceled: default: f.Logger.Err("failed to fetch config from %s: %v", name, err) } errChan <- err return } data = raw cancel() }() } dispatch("config drive (config)", func() ([]byte, error) { return fetchConfigFromDevice(f.Logger, ctx, "config-2") }) dispatch("config drive (CONFIG)", func() ([]byte, error) { return fetchConfigFromDevice(f.Logger, ctx, "CONFIG-2") }) dispatch("metadata service", func() ([]byte, error) { return fetchConfigFromMetadataService(f) }) Loop: for { select { case <-ctx.Done(): break Loop case <-errChan: dispatchCount-- if dispatchCount == 0 { f.Logger.Info("couldn't fetch config") break Loop } } } return util.ParseConfig(f.Logger, data) } func fileExists(path string) bool { _, err := os.Stat(path) return (err == nil) } func labelExists(label string) bool { _, err := getPath(label) return (err == nil) } func getPath(label string) (string, error) { path := filepath.Join(distro.DiskByLabelDir(), label) if fileExists(path) { return path, nil } return "", fmt.Errorf("label not found: %s", label) } func findLease() (*os.File, error) { ifaces, err := net.Interfaces() if err != nil { return nil, fmt.Errorf("could not list interfaces: %v", err) } for { for _, iface := range ifaces { lease, err := os.Open(fmt.Sprintf("/run/systemd/netif/leases/%d", iface.Index)) if os.IsNotExist(err) { continue } else if err != nil { return nil, err } else { return lease, nil } } fmt.Printf("No leases found. Waiting...") time.Sleep(LeaseRetryInterval) } } func getDHCPServerAddress() (string, error) { lease, err := findLease() if err != nil { return "", err } defer func() { _ = lease.Close() }() var address string line := bufio.NewScanner(lease) for line.Scan() { parts := strings.Split(line.Text(), "=") if parts[0] == "SERVER_ADDRESS" && len(parts) == 2 { address = parts[1] break } } if len(address) == 0 { return "", fmt.Errorf("dhcp server address not found in leases") } return address, nil } func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, label string) ([]byte, error) { for !labelExists(label) { logger.Debug("config drive (%q) not found. Waiting...", label) select { case <-time.After(time.Second): case <-ctx.Done(): return nil, ctx.Err() } } path, err := getPath(label) if err != nil { return nil, err } logger.Debug("creating temporary mount point") mnt, err := os.MkdirTemp("", "ignition-configdrive") if err != nil { return nil, fmt.Errorf("failed to create temp directory: %v", err) } defer func() { if removeErr := os.Remove(mnt); removeErr != nil { logger.Warning("failed to remove temp directory %q: %v", mnt, removeErr) } }() cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", path, mnt) if _, err := logger.LogCmd(cmd, "mounting config drive"); err != nil { return nil, err } defer func() { _ = logger.LogOp( func() error { return ut.UmountPath(mnt) }, "unmounting %q at %q", path, mnt, ) }() if !fileExists(filepath.Join(mnt, configDriveUserdataPath)) { return nil, nil } return os.ReadFile(filepath.Join(mnt, configDriveUserdataPath)) } func fetchConfigFromMetadataService(f *resource.Fetcher) ([]byte, error) { addr, err := getDHCPServerAddress() if err != nil { return nil, err } metadataServiceUrl := url.URL{ Scheme: "http", Host: addr, Path: "/latest/user-data", } res, err := f.FetchToBuffer(metadataServiceUrl, resource.FetchOptions{}) // the metadata server exists but doesn't contain any actual metadata, // assume that there is no config specified if err == resource.ErrNotFound { return nil, nil } return res, err }