Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 9 additions & 26 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
Changelog file generated automatically. Do not edit.
Changelog generated at: 2025-10-28T12:36:32+00:00
Changelog generated at: 2025-11-25T19:36:54+00:00
# Changelog

## [Unreleased](https://github.com/fujitsu/docker-machine-driver-fsas/tree/HEAD)
## [v0.1.9](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.9) (2025-11-24)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.7...HEAD)
[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.8...v0.1.9)

**Merged pull requests:**

- Merged PR 1776: feature/handle\_sles\_registration [\#17](https://github.com/fujitsu/docker-machine-driver-fsas/pull/17) ([fujitsu-domzalskis](https://github.com/fujitsu-domzalskis))
- Feature/set explicitly permisions in workflow files [\#16](https://github.com/fujitsu/docker-machine-driver-fsas/pull/16) ([lukasz-piotrowski-fujitsu](https://github.com/lukasz-piotrowski-fujitsu))
- add log extension to gitignore; [\#15](https://github.com/fujitsu/docker-machine-driver-fsas/pull/15) ([lukasz-piotrowski-fujitsu](https://github.com/lukasz-piotrowski-fujitsu))
- Deregister only registered SLES product [\#19](https://github.com/fujitsu/docker-machine-driver-fsas/pull/19) ([AnjriI](https://github.com/AnjriI))

## [v0.1.8](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.8) (2025-10-29)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.7...v0.1.8)

## [v0.1.7](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.7) (2025-10-07)

Expand All @@ -20,37 +22,18 @@ Changelog generated at: 2025-10-28T12:36:32+00:00

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.5...v0.1.6-hotfix-cdi-746)

**Merged pull requests:**

- Merged PR 1612: Implementation of GPU node labels [\#7](https://github.com/fujitsu/docker-machine-driver-fsas/pull/7) ([AnjriI](https://github.com/AnjriI))

## [v0.1.5](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.5) (2025-10-03)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.4...v0.1.5)

**Merged pull requests:**

- new workflows for testing, creating release and generating changelog … [\#6](https://github.com/fujitsu/docker-machine-driver-fsas/pull/6) ([lukasz-piotrowski-fujitsu](https://github.com/lukasz-piotrowski-fujitsu))

## [v0.1.4](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.4) (2025-09-23)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.3-hotfix-cdi-802...v0.1.4)

**Merged pull requests:**

- handle situation when CDI status cannot be found in statuses assignment map; [\#5](https://github.com/fujitsu/docker-machine-driver-fsas/pull/5) ([lukasz-piotrowski-fujitsu](https://github.com/lukasz-piotrowski-fujitsu))

## [v0.1.3-hotfix-cdi-802](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.3-hotfix-cdi-802) (2025-09-19)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.2-hotfix-cdi-817...v0.1.3-hotfix-cdi-802)

**Merged pull requests:**

- Merged PR 1451: Implement graceful shutdown using FM API [\#4](https://github.com/fujitsu/docker-machine-driver-fsas/pull/4) ([AnjriI](https://github.com/AnjriI))
- Fix/cdi-527 [\#3](https://github.com/fujitsu/docker-machine-driver-fsas/pull/3) ([lukasz-piotrowski-fujitsu](https://github.com/lukasz-piotrowski-fujitsu))
- Merged PR 1477: Applying singular of CLIENT\_SECRETS and relevant across codebase [\#2](https://github.com/fujitsu/docker-machine-driver-fsas/pull/2) ([AnjriI](https://github.com/AnjriI))
- Add pipeline into Github repository [\#1](https://github.com/fujitsu/docker-machine-driver-fsas/pull/1) ([fujitsu-domzalskis](https://github.com/fujitsu-domzalskis))

## [v0.1.2-hotfix-cdi-817](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.2-hotfix-cdi-817) (2025-09-04)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/v0.1.1-hotfix-cdi-805...v0.1.2-hotfix-cdi-817)
Expand All @@ -61,7 +44,7 @@ Changelog generated at: 2025-10-28T12:36:32+00:00

## [v0.1.0](https://github.com/fujitsu/docker-machine-driver-fsas/tree/v0.1.0) (2025-09-01)

[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/5f755625641ed1da2a127e80c54c38286efb4a7d...v0.1.0)
[Full Changelog](https://github.com/fujitsu/docker-machine-driver-fsas/compare/2be96e8ace1161aed15eb202341d78786306e639...v0.1.0)



Expand Down
95 changes: 91 additions & 4 deletions cfgutils/cfgutils.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
package cfgutils

import (
"bytes"
"encoding/json"
"fmt"
"os"
"strings"

slog "github.com/fujitsu/docker-machine-driver-fsas/logger"
"github.com/fujitsu/docker-machine-driver-fsas/models"
"gopkg.in/yaml.v3"
)

var (
isInit = false
isInit = false
osWriteFile = os.WriteFile
osReadFile = os.ReadFile
osStat = os.Stat
)

// CfgManager interface defines the methods for interacting with the Configuration Manager.
type CfgManager interface {
IsInit() bool
PrepareMetadata(instanceId, hostname string) string
PrepareRke2ConfigScript(configName, machineUUID string) string
ExtendUserdataRunCmd(commands []string) error
ExtendUserdataWriteFiles(fileObjects []CloudConfigItem) error
}

// StandardCfgManager struct holds configuration for Configuration Manager interaction.
type StandardCfgManager struct {
resources []models.Resource
resources []models.Resource
userDataFile string
}

var _ CfgManager = (*StandardCfgManager)(nil)

// NewStandardCfgManager Returns new instance of Standard Configuration Manager
func NewStandardCfgManager(devicesSpecJson string) *StandardCfgManager {
func NewStandardCfgManager(devicesSpecJson, userDataFile string) *StandardCfgManager {
var resources []models.Resource
if err := json.Unmarshal([]byte(devicesSpecJson), &resources); err != nil {
slog.Warn("Failed to parse DevicesSpecJson, proceeding with empty resources: ", "err", err)
resources = []models.Resource{}
}

isInit = true
return &StandardCfgManager{resources: resources}
return &StandardCfgManager{resources: resources, userDataFile: userDataFile}
}

// IsInit Returns true if constructor succeed else false
Expand All @@ -58,6 +68,7 @@ func (sc *StandardCfgManager) PrepareRke2ConfigScript(configName, machineUUID st
slog.Debug(fmt.Sprintf("Prepare RKE2 Config Script: %s", configName))
providerIdEntry := sc.prepareRke2ConfigProviderId(machineUUID)
nodeLabelEntry := sc.prepareRke2ConfigNodeLabelsForGpu()

var configContent string
if nodeLabelEntry != "" {
configContent = fmt.Sprintf("%s\n%s", providerIdEntry, nodeLabelEntry)
Expand Down Expand Up @@ -91,6 +102,7 @@ func (sc *StandardCfgManager) prepareRke2ConfigProviderId(MachineUUID string) st
// prepareRke2ConfigNodeLabelsForGpu returns a string with node labels
func (sc *StandardCfgManager) prepareRke2ConfigNodeLabelsForGpu() string {
slog.Debug("Prepare RKE2 Config Node Labels")

// GPU map (short names to full names)
allowedGPUs := map[string]string{
"nvidia-a100-40g": "nvidia-a100-40g",
Expand All @@ -100,41 +112,116 @@ func (sc *StandardCfgManager) prepareRke2ConfigNodeLabelsForGpu() string {
"a100-80g": "nvidia-a100-80g",
"h100": "nvidia-h100",
}

labels := []string{}

for _, res := range sc.resources {
if res.ResourceType != "gpu" || res.ResourceSpec == nil {
continue
}

model := ""
for _, cond := range res.ResourceSpec.Condition {
if cond.Column == "model" && cond.Operator == "eq" {
model = cond.Value
break
}
}

fullModel, ok := allowedGPUs[model]
if !ok {
slog.Warn("Skipping labels because GPU model not allowed: ", "value", model)
continue
}

if res.MinResourceCount > res.MaxResourceCount {
slog.Warn("Invalid GPU config: MinResourceCount > MaxResourceCount ", "model", fullModel, "min", res.MinResourceCount, "max", res.MaxResourceCount)
continue
}

if res.MinResourceCount > 0 {
labels = append(labels, fmt.Sprintf("cohdi.io/%s-size-min=%d", fullModel, res.MinResourceCount))
} else {
slog.Warn("MinResourceCount missing for GPU: ", "model", fullModel)
}

if res.MaxResourceCount > 0 {
labels = append(labels, fmt.Sprintf("cohdi.io/%s-size-max=%d", fullModel, res.MaxResourceCount))
} else {
slog.Warn("MaxResourceCount missing for GPU: ", "model", fullModel)
}
}

if len(labels) == 0 {
slog.Debug("No GPU labels generated because of empty GPU resources")
return ""
}

return fmt.Sprintf(`kubelet-arg+: "node-labels=%s"`, strings.Join(labels, ","))
}

func (sc *StandardCfgManager) ExtendUserdataRunCmd(commands []string) error {
cloudConfigItems := []CloudConfigItem{NewCloudConfigItemRunCmd(commands)}
return sc.extendUserdata(cloudConfigItems)
}

func (sc *StandardCfgManager) ExtendUserdataWriteFiles(fileObjects []CloudConfigItem) error {
return sc.extendUserdata(fileObjects)
}

// extendUserdata Extends cloud config userdata file
func (sc *StandardCfgManager) extendUserdata(cci []CloudConfigItem) error {
if sc.userDataFile == "" {
return nil
}

if _, err := osStat(sc.userDataFile); os.IsNotExist(err) {
slog.Error("User data file does not exist:", "path", sc.userDataFile, "err", err)
return err
}

userdata, err := osReadFile(sc.userDataFile)
if err != nil {
return err
}

cloudConfig := make(map[string]interface{})
if err := yaml.Unmarshal(userdata, &cloudConfig); err != nil {
return err
}

for _, ccItem := range cci {
moduleName := ccItem.getModuleName()

newContent, err := ccItem.getNewCloudConfigContent()
if err != nil {
return fmt.Errorf("error while appending userdata file; module= %s: %w", moduleName, err)
}

existing, ok := cloudConfig[moduleName]
if !ok {
cloudConfig[moduleName] = newContent
continue
}

slice, ok := existing.([]interface{})
if !ok {
return fmt.Errorf("module %s exists but is not a list", moduleName)
}

cloudConfig[moduleName] = append(slice, newContent...)
}

yamlBytes, err := yaml.Marshal(cloudConfig)
if err != nil {
return err
}

trimmed := bytes.TrimSpace(yamlBytes)

if !bytes.HasPrefix(trimmed, []byte("#cloud-config")) {
trimmed = append([]byte("#cloud-config\n"), trimmed...)
}

return osWriteFile(sc.userDataFile, trimmed, writeFilePermissions)
}
Loading