Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b2e2c14
Preflight: Create helper for set and get the capabilities
praveenkumar Nov 21, 2025
8a8f59e
Cached gvproxy to consume with macadam
praveenkumar Nov 14, 2025
90611e0
Cached macadam binary
praveenkumar Nov 17, 2025
47f291a
Add Logic to have different filename for cache
praveenkumar Nov 18, 2025
90bee86
ssh: Ensure parent directory exist before keypair generation
praveenkumar Nov 21, 2025
afaf6f5
cluster: Don't apply pull secret patch
praveenkumar Nov 21, 2025
93b1008
Remove libmachine api and use macadam binary
praveenkumar Nov 28, 2025
6125fb7
systemd: Add result helper to get service exit result
praveenkumar Dec 16, 2025
a7ef31f
systemd: Add helper to get service exit timestamp
praveenkumar Dec 16, 2025
4efe7a7
vsock: Use gvclient to expose dns and ports
praveenkumar Dec 16, 2025
35ec486
cluster: Add WaitForServiceSuccessfullyFinished helper
praveenkumar Dec 16, 2025
5c38796
start: Copy the kubeconfig file from VM to host instead update
praveenkumar Dec 16, 2025
a441f16
Fixup: fix unit test for macadam and preflight
praveenkumar Dec 16, 2025
84e24da
Fixup: make golint happy
praveenkumar Dec 16, 2025
1e46a51
feat: Add cursor commit rules documentation
praveenkumar Jan 8, 2026
f8a9059
build: Update source file discovery in Makefile
praveenkumar Jan 8, 2026
4135f8d
refactor: Remove libvirt integration and update preflight checks
praveenkumar Jan 8, 2026
7308267
vendor: Remove libvirt dependency and related files
praveenkumar Jan 8, 2026
e18270d
feat(systemd): Add WasSkippedDueToConditions method and handle skippe…
praveenkumar Jan 8, 2026
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
67 changes: 67 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Cursor Commit Rules for crc-org/crc

When generating git commit messages:
- Follow the Conventional Commits specification format:
<type>[optional scope]: <subject>
[blank line]
[optional body explaining what changed and why]
[optional footer with references, breaking changes]
- Use **imperative** and **present tense** in the subject line.
- Limit the **subject line to ≈50–72 characters**; wrap body at ≈72.
- Use meaningful scopes when it clarifies affected modules (e.g., `pkg`, `cmd`, `docs`, `test`, `tools`).
- Prefer concise yet descriptive wording of **what changed** and **why**, not just **how**.
- When applicable, reference related issues or pull request numbers in footer.

# Allowed Commit Types
feat: add user-visible functionality or significant enhancements
fix: bug fix
docs: documentation only (e.g., README, docs/)
style: formatting, whitespace, linting (no code logic change)
refactor: code change that neither fixes a bug nor adds a feature
perf: performance improvement
test: adding or fixing tests
ci: continuous integration/config change (e.g., GitHub Actions)
build: build system or external dependency changes
chore: routine tasks without code change (scripts, config)

# Type Rules
feat:
- New CLI subcommands or flags
- New OpenShift, MicroShift, or podman integration options

fix:
- Correct unintended behaviors in cluster start/stop logic
- Resolve crashes, error cases, CLI UX bugs

docs:
- Add or update CRC documentation pages
- Fix README typos or incorrect instructions

style:
- Adjust formatting (go fmt), remove unused imports
- Linters fixes; no functional impact

refactor:
- Extract utils, reorganize package structure
- Rename internal functions for clarity

perf:
- Optimize CPU/memory usage in cluster operations

test:
- Add or improve unit/integration tests
- Update CI test matrix

ci:
- CI workflow fixes or adjustments (e.g., GitHub workflows)

build:
- Update Go module versions
- Changes to build scripts (Makefile, tooling)

chore:
- Non-code housekeeping (dependency cleanup, templating)

# Footer Conventions
- Close issues with “Closes #<number>.”
- Denote breaking changes via `BREAKING CHANGE:` in body/footer.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ORG := github.com/crc-org
MODULEPATH = $(ORG)/crc/v2
PACKAGE_DIR := packaging/$(GOOS)

SOURCES := $(shell git ls-files '*.go' ":^vendor")
SOURCES := $(shell find . -name '*.go' -not -path './vendor/*' -not -path './tools/vendor/*')
SOURCES := $(SOURCES) go.mod go.sum Makefile

RELEASE_INFO := release-info.json
Expand Down
5 changes: 1 addition & 4 deletions cmd/crc-embedder/cmd/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/crc-org/crc/v2/pkg/crc/logging"
"github.com/crc-org/crc/v2/pkg/download"

"github.com/crc-org/crc/v2/pkg/crc/machine/libvirt"
"github.com/crc-org/crc/v2/pkg/crc/machine/vfkit"

"github.com/YourFin/binappend"
Expand All @@ -30,7 +29,6 @@ var (
const (
vfkitDriver = "vfkit-driver"
vfkitEntitlement = "vfkit-entitlement"
libvirtDriver = "libvirt-driver"
adminHelper = "admin-helper"
backgroundLauncher = "background-launcher"
)
Expand Down Expand Up @@ -114,8 +112,7 @@ var (
adminHelper: {constants.GetAdminHelperURLForOs("darwin"), 0755},
},
"linux": {
libvirtDriver: {libvirt.MachineDriverDownloadURL, 0755},
adminHelper: {constants.GetAdminHelperURLForOs("linux"), 0755},
adminHelper: {constants.GetAdminHelperURLForOs("linux"), 0755},
},
"windows": {
adminHelper: {constants.GetAdminHelperURLForOs("windows"), 0755},
Expand Down
10 changes: 6 additions & 4 deletions cmd/crc/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ import (
"github.com/crc-org/crc/v2/pkg/crc/cluster"
crcConfig "github.com/crc-org/crc/v2/pkg/crc/config"
"github.com/crc-org/crc/v2/pkg/crc/constants"
"github.com/crc-org/crc/v2/pkg/crc/daemonclient"
crcErrors "github.com/crc-org/crc/v2/pkg/crc/errors"
"github.com/crc-org/crc/v2/pkg/crc/logging"
"github.com/crc-org/crc/v2/pkg/crc/machine/bundle"
"github.com/crc-org/crc/v2/pkg/crc/machine/types"
"github.com/crc-org/crc/v2/pkg/crc/network"
"github.com/crc-org/crc/v2/pkg/crc/preflight"
"github.com/crc-org/crc/v2/pkg/crc/preset"
"github.com/crc-org/crc/v2/pkg/crc/validation"
Expand Down Expand Up @@ -94,9 +92,10 @@ func runStart(ctx context.Context) (*types.StartResult, error) {
isRunning, _ := client.IsRunning()

if !isRunning {
if err := checkDaemonStarted(); err != nil {
// TODO: Uncomment this when we need it only for admin-helper
/*if err := checkDaemonStarted(); err != nil {
return nil, err
}
}*/

if err := preflight.StartPreflightChecks(config); err != nil {
return nil, crcos.CodeExitError{
Expand Down Expand Up @@ -328,6 +327,8 @@ func commandLinePrefix(shell string) string {
return "$"
}

// TODO: Uncomment this when we need it only for admin-helper
/*
func checkDaemonStarted() error {
if crcConfig.GetNetworkMode(config) == network.SystemNetworkingMode {
return nil
Expand All @@ -338,6 +339,7 @@ func checkDaemonStarted() error {
}
return daemonclient.CheckVersionMismatch(v)
}
*/

func portFallbackWarning() string {
var fallbackPortWarning string
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ require (
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/client-go v0.32.1
libvirt.org/go/libvirtxml v1.11010.0
)

require (
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -619,8 +619,6 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJ
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
libvirt.org/go/libvirtxml v1.11010.0 h1:lGUv6OQ4gz5Hm7F40G+swxmK/kcrMZGQ3M8/S+UyhME=
libvirt.org/go/libvirtxml v1.11010.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
Expand Down
46 changes: 45 additions & 1 deletion pkg/crc/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Cache struct {
version string
ignoreNameMismatch bool
getVersion func(string) (string, error)
targetName string // Optional: if set, rename the executable to this name
}

type VersionMismatchError struct {
Expand Down Expand Up @@ -84,6 +85,44 @@ func NewAdminHelperCache() *Cache {
)
}

func NewGvproxyCache() *Cache {
url := constants.GetGvproxyURL()
version := version.GetGvproxyVersion()
cache := newCache(constants.GvproxyPath(),
url,
version,
func(executable string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewAdminHelperCache uses exactly the same function, might be desirable to share that code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can share the code for getting the version info. May be a followup or new commit.

out, _, err := crcos.RunWithDefaultLocale(executable, "--version")
if err != nil {
return "", err
}
// gvproxy --version output format: "gvproxy version v0.8.7"
split := strings.Split(out, " ")
return strings.TrimSpace(split[len(split)-1]), nil
},
)
cache.targetName = constants.GetGvproxyExecutableName()
return cache
}

func NewMacadamCache() *Cache {
url := constants.GetMacadamURL()
version := version.GetMacadamVersion()
return newCache(constants.MacadamPath(),
url,
version,
func(executable string) (string, error) {
out, _, err := crcos.RunWithDefaultLocale(executable, "--version")
if err != nil {
return "", err
}
// macadam version output format: "macadam version v0.2.0"
split := strings.Split(out, " ")
return strings.TrimSpace(split[len(split)-1]), nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for NewAdminHelperCache and NewGvproxyCache

},
)
}

func (c *Cache) IsCached() bool {
if _, err := os.Stat(c.GetExecutablePath()); os.IsNotExist(err) {
return false
Expand Down Expand Up @@ -136,7 +175,12 @@ func (c *Cache) cacheExecutable() error {

// Copy the requested asset into its final destination
for _, extractedFilePath := range extractedFiles {
finalExecutablePath := filepath.Join(constants.CrcBinDir, c.GetExecutableName())
// Use targetName if set, otherwise use the original executable name
finalName := c.GetExecutableName()
if c.targetName != "" {
finalName = c.targetName
}
finalExecutablePath := filepath.Join(constants.CrcBinDir, finalName)
// If the file exists then remove it (ignore error) first before copy because with `0500` permission
// it is not possible to overwrite the file.
os.Remove(finalExecutablePath)
Expand Down
13 changes: 0 additions & 13 deletions pkg/crc/cache/cache_linux.go

This file was deleted.

106 changes: 106 additions & 0 deletions pkg/crc/cloudinit/cloudinit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package cloudinit

import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"

"github.com/crc-org/crc/v2/pkg/crc/constants"
)

// UserDataOptions contains all the options needed to generate cloud-init user-data
type UserDataOptions struct {
PublicKey string
PullSecret string
KubeAdminPassword string
DeveloperPassword string
}

const userDataTemplate = `#cloud-config
runcmd:
- systemctl enable --now kubelet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And ideally this would also not be needed…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cfergeau why? this is required because our bundle have kubelet service disabled by default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer as #5025 (comment)
"ideally" = would be nice to improve the self sufficient bundle and make the cloud-init file simpler.

write_files:
- path: /home/core/.ssh/authorized_keys
content: '%s'
owner: core
permissions: '0600'
- path: /opt/crc/id_rsa.pub
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this go in /home/core/.ssh/ like authorized_keys?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, /opt/crc/id_rsa.pub is the file which is required by mco ssh service https://github.com/crc-org/snc/blob/release-4.20/systemd/ocp-mco-sshkey.service here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a document for the cloud-init file we expect, together with an explanation about what uses each cloud-init "parameter"?

content: '%s'
owner: root:root
permissions: '0644'
- path: /etc/sysconfig/crc-env
content: |
CRC_SELF_SUFFICIENT=1
CRC_NETWORK_MODE_USER=1
owner: root:root
permissions: '0644'
- path: /opt/crc/pull-secret
content: |
%s
permissions: '0644'
- path: /opt/crc/pass_kubeadmin
content: '%s'
permissions: '0644'
- path: /opt/crc/pass_developer
content: '%s'
permissions: '0644'
- path: /opt/crc/ocp-custom-domain.service.done
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this would not be needed… It’s when we want a custom domain that something would appear in the cloud-init file. But for the nominal case, we should not have to add magic files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise https://github.com/crc-org/snc/blob/release-4.20/systemd/ocp-custom-domain.service will fails and since it is Restart=on-failure so it is going to continue fail :( so better to have it as this magic file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that this would be a nice improvement to make to the self sufficient bundle… The less magic is required in the cloud-init file, the better

permissions: '0644'
`

// compactJSON compacts a JSON string by removing whitespace and newlines
func compactJSON(jsonStr string) (string, error) {
var buf bytes.Buffer
if err := json.Compact(&buf, []byte(jsonStr)); err != nil {
return "", fmt.Errorf("failed to compact JSON: %w", err)
}
return buf.String(), nil
}

// GenerateUserData generates a cloud-init user-data file and returns the path
func GenerateUserData(machineName string, opts UserDataOptions) (string, error) {
// Create the machine directory if it doesn't exist
machineDir := filepath.Dir(getUserDataPath(machineName))
if err := os.MkdirAll(machineDir, 0o750); err != nil {
return "", fmt.Errorf("failed to create machine directory: %w", err)
}

// Compact the pull secret JSON
compactPullSecret, err := compactJSON(opts.PullSecret)
if err != nil {
return "", fmt.Errorf("failed to compact pull secret: %w", err)
}

// Generate the cloud-init user-data content
userData := fmt.Sprintf(userDataTemplate,
opts.PublicKey, // /home/core/.ssh/authorized_keys
opts.PublicKey, // /opt/crc/id_rsa.pub
compactPullSecret, // /opt/crc/pull-secret (compacted)
opts.KubeAdminPassword, // /opt/crc/pass_kubeadmin
opts.DeveloperPassword, // /opt/crc/pass_developer
)

userDataPath := getUserDataPath(machineName)
// Write the user-data file
if err := os.WriteFile(userDataPath, []byte(userData), 0o600); err != nil {
return "", fmt.Errorf("failed to write user-data file: %w", err)
}

return userDataPath, nil
}

// RemoveUserData removes the cloud-init user-data file for a machine
func RemoveUserData(machineName string) error {
userDataPath := filepath.Join(constants.MachineInstanceDir, machineName, "user-data")
if err := os.Remove(userDataPath); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to remove user-data file: %w", err)
}
return nil
}

// getUserDataPath returns the path to the user-data file for a machine
func getUserDataPath(machineName string) string {
return filepath.Join(constants.MachineInstanceDir, machineName, "user-data")
}
Loading
Loading