Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
73ddf18
:seedling:(deps): Bump softprops/action-gh-release
dependabot[bot] Jun 16, 2025
c5ad328
Merge pull request #2597 from kubernetes-sigs/dependabot/github_actio…
k8s-ci-robot Jun 17, 2025
3e1885e
Bump go to 1.23.10
lentzi90 Jun 16, 2025
5113b31
Merge pull request #2599 from Nordix/lentzi90/release-0.11-go-bump
k8s-ci-robot Jun 18, 2025
d5447b0
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 3 …
dependabot[bot] Jun 23, 2025
1a739ba
Update generated code
dependabot[bot] Jun 23, 2025
c56b58a
Merge pull request #2603 from kubernetes-sigs/dependabot/go_modules/r…
k8s-ci-robot Jun 24, 2025
99bbbae
Update templates
lentzi90 Jun 24, 2025
c9b797f
Merge pull request #2606 from k8s-infra-cherrypick-robot/cherry-pick-…
k8s-ci-robot Jun 24, 2025
bf9e9cc
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 3 …
dependabot[bot] Jul 21, 2025
2fbb453
Update generated code
dependabot[bot] Jul 21, 2025
542b69b
Ignore irrelevant CVE-2025-22868
lentzi90 Jul 28, 2025
6b8a7a0
Merge pull request #2632 from Nordix/lentzi90/add-ignore-release-0.11
k8s-ci-robot Jul 31, 2025
8bd6bf5
Merge pull request #2625 from kubernetes-sigs/dependabot/go_modules/r…
k8s-ci-robot Aug 4, 2025
def7861
E2E: Update Calico to v3.30.2
lentzi90 Jun 25, 2025
b701d4a
Merge pull request #2643 from k8s-infra-cherrypick-robot/cherry-pick-…
k8s-ci-robot Aug 7, 2025
cf5d71f
:seedling:(deps): Bump the all-github-actions group with 2 updates
dependabot[bot] Aug 12, 2025
b1f30d2
Merge pull request #2648 from kubernetes-sigs/dependabot/github_actio…
k8s-ci-robot Aug 12, 2025
abaf589
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 3 …
dependabot[bot] Aug 13, 2025
d78aaa4
Update generated code
dependabot[bot] Aug 13, 2025
b0285a7
Merge pull request #2655 from kubernetes-sigs/dependabot/go_modules/r…
k8s-ci-robot Aug 14, 2025
d46eeb4
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 1 …
dependabot[bot] Aug 19, 2025
34788df
Merge pull request #2661 from kubernetes-sigs/dependabot/go_modules/h…
k8s-ci-robot Aug 19, 2025
d6dd0ea
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 2 …
dependabot[bot] Aug 26, 2025
94b7866
Merge pull request #2670 from kubernetes-sigs/dependabot/go_modules/r…
k8s-ci-robot Aug 29, 2025
c48fd6f
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 3 …
dependabot[bot] Sep 2, 2025
f5e1d4a
Update generated code
dependabot[bot] Sep 2, 2025
e955814
Merge pull request #2678 from kubernetes-sigs/dependabot/go_modules/r…
k8s-ci-robot Sep 2, 2025
9feb382
Fix panic when OpenStack server is deleted by an external agent
mdbooth Mar 11, 2025
38bdee3
Don't set OSMachine Ready until all config is complete
mdbooth Mar 14, 2025
b7ac5cd
corrected lint requirements
TruongHoangPhuLoc Sep 2, 2025
dd61c2a
Merge pull request #2690 from k8s-infra-cherrypick-robot/cherry-pick-…
k8s-ci-robot Sep 8, 2025
7765f53
:seedling:(deps): Bump the all-github-actions group with 2 updates
dependabot[bot] Sep 8, 2025
0766202
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 3 …
dependabot[bot] Sep 8, 2025
8a997b8
Update generated code
dependabot[bot] Sep 8, 2025
717fc8c
Merge pull request #2698 from kubernetes-sigs/dependabot/github_actio…
k8s-ci-robot Sep 8, 2025
1615648
Merge pull request #2700 from kubernetes-sigs/dependabot/go_modules/r…
k8s-ci-robot Sep 8, 2025
2b851e6
Uplift go 1.23.12 to address security issue in release-0.12
smoshiur1237 Sep 9, 2025
20bcb0f
Merge pull request #2712 from k8s-infra-cherrypick-robot/cherry-pick-…
k8s-ci-robot Sep 9, 2025
46194f2
changes for 0.11 release
okozachenko1203 Sep 9, 2025
fc4946c
Merge pull request #2685 from okozachenko1203/release-0.11-fix-panic-…
k8s-ci-robot Sep 12, 2025
4638434
:seedling:(deps): Bump the all-go-mod-patch-and-minor group across 1 …
dependabot[bot] Sep 15, 2025
494556c
Merge pull request #2722 from kubernetes-sigs/dependabot/go_modules/h…
k8s-ci-robot Sep 18, 2025
dfe54f0
Merge https://github.com/kubernetes-sigs/cluster-api-provider-opensta…
Sep 18, 2025
a96f277
Updating and vendoring go modules after an upstream merge
Sep 18, 2025
4317058
CARRY: running make merge-bot
Sep 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .github/workflows/pr-dependabot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # tag=v5.0.0
- name: Calculate go version
id: vars
run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # tag=v6.0.0
with:
go-version: ${{ steps.vars.outputs.go_version }}
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # tag=v4.2.3
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # tag=v4.2.4
name: Restore go cache
with:
path: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ jobs:
- name: Set env
run: echo "RELEASE_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV
- name: checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # tag=v5.0.0
with:
fetch-depth: 0
- name: Calculate go version
run: echo "go_version=$(make go-version)" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # tag=v6.0.0
with:
go-version: ${{ env.go_version }}
- name: generate release artifacts
Expand All @@ -37,7 +37,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Release
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # tag=v2.2.2
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # tag=v2.3.3
with:
draft: true
files: out/*
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/security-scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # tag=v5.0.0
with:
ref: ${{ matrix.branch }}
- name: Calculate go version
id: vars
run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # tag=v6.0.0
with:
go-version: ${{ steps.vars.outputs.go_version }}
- name: Run verify security target
Expand Down
1 change: 1 addition & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
# According to govulncheck we are not using code that is affected by them anyway
CVE-2025-22870
CVE-2025-22872
CVE-2025-22868
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ unexport GOPATH
TRACE ?= 0

# Go
GO_VERSION ?= 1.23.8
GO_VERSION ?= 1.23.12

# Directories.
ARTIFACTS ?= $(REPO_ROOT)/_artifacts
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta1/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const (
OpenStackErrorReason = "OpenStackError"
// DependencyFailedReason indicates that a dependent object failed.
DependencyFailedReason = "DependencyFailed"

// ServerUnexpectedDeletedMessage is the message used when the server is unexpectedly deleted via an external agent.
ServerUnexpectedDeletedMessage = "The server was unexpectedly deleted"
)

const (
Expand Down
26 changes: 15 additions & 11 deletions controllers/openstackmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,12 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope
return ctrl.Result{}, err
}

if instanceStatus == nil {
conditions.MarkFalse(openStackMachine, infrav1.InstanceReadyCondition, infrav1.InstanceDeletedReason, clusterv1.ConditionSeverityError, infrav1.ServerUnexpectedDeletedMessage)
openStackMachine.SetFailure(capierrors.UpdateMachineError, errors.New(infrav1.ServerUnexpectedDeletedMessage)) //nolint:stylecheck // This error is not used as an error
return ctrl.Result{}, nil
}

instanceNS, err := instanceStatus.NetworkStatus()
if err != nil {
return ctrl.Result{}, fmt.Errorf("get network status: %w", err)
Expand All @@ -393,22 +399,20 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope
})
openStackMachine.Status.Addresses = addresses

result := r.reconcileMachineState(scope, openStackMachine, machine, machineServer)
if result != nil {
return *result, nil
}
if util.IsControlPlaneMachine(machine) {
err = r.reconcileAPIServerLoadBalancer(scope, openStackCluster, openStackMachine, instanceStatus, instanceNS, clusterResourceName)
if err != nil {
return ctrl.Result{}, err
}

if !util.IsControlPlaneMachine(machine) {
scope.Logger().Info("Not a Control plane machine, no floating ip reconcile needed, Reconciled Machine create successfully")
return ctrl.Result{}, nil
conditions.MarkTrue(openStackMachine, infrav1.APIServerIngressReadyCondition)
}

err = r.reconcileAPIServerLoadBalancer(scope, openStackCluster, openStackMachine, instanceStatus, instanceNS, clusterResourceName)
if err != nil {
return ctrl.Result{}, err
result := r.reconcileMachineState(scope, openStackMachine, machine, machineServer)
if result != nil {
return *result, nil
}

conditions.MarkTrue(openStackMachine, infrav1.APIServerIngressReadyCondition)
scope.Logger().Info("Reconciled Machine create successfully")
return ctrl.Result{}, nil
}
Expand Down
22 changes: 14 additions & 8 deletions controllers/openstackserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,18 @@ func (r *OpenStackServerReconciler) getOrCreateServer(ctx context.Context, logge

if openStackServer.Status.InstanceID != nil {
instanceStatus, err = computeService.GetInstanceStatus(*openStackServer.Status.InstanceID)
if err != nil {
logger.Info("Unable to get OpenStack instance", "name", openStackServer.Name)
conditions.MarkFalse(openStackServer, infrav1.InstanceReadyCondition, infrav1.OpenStackErrorReason, clusterv1.ConditionSeverityError, "%s", err.Error())
if err != nil || instanceStatus == nil {
logger.Info("Unable to get OpenStack instance", "name", openStackServer.Name, "id", *openStackServer.Status.InstanceID)
var msg string
var reason string
if err != nil {
msg = err.Error()
reason = infrav1.OpenStackErrorReason
} else {
msg = infrav1.ServerUnexpectedDeletedMessage
reason = infrav1.InstanceNotFoundReason
}
conditions.MarkFalse(openStackServer, infrav1.InstanceReadyCondition, reason, clusterv1.ConditionSeverityError, msg)
return nil, err
}
}
Expand All @@ -450,11 +459,6 @@ func (r *OpenStackServerReconciler) getOrCreateServer(ctx context.Context, logge
logger.Info("Server already exists", "name", openStackServer.Name, "id", instanceStatus.ID())
return instanceStatus, nil
}
if openStackServer.Status.InstanceID != nil {
logger.Info("Not reconciling server in failed state. The previously existing OpenStack instance is no longer available")
conditions.MarkFalse(openStackServer, infrav1.InstanceReadyCondition, infrav1.InstanceNotFoundReason, clusterv1.ConditionSeverityError, "virtual machine no longer exists")
return nil, nil
}

logger.Info("Server does not exist, creating Server", "name", openStackServer.Name)
instanceSpec, err := r.serverToInstanceSpec(ctx, openStackServer)
Expand All @@ -468,6 +472,8 @@ func (r *OpenStackServerReconciler) getOrCreateServer(ctx context.Context, logge
openStackServer.Status.InstanceState = &infrav1.InstanceStateError
return nil, fmt.Errorf("create OpenStack instance: %w", err)
}
// We reached a point where a server was created with no error but we can't predict its state yet which is why we don't update conditions yet.
// The actual state of the server is checked in the next reconcile loops.
return instanceStatus, nil
}
return instanceStatus, nil
Expand Down
184 changes: 184 additions & 0 deletions controllers/openstackserver_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ import (
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports"
. "github.com/onsi/gomega" //nolint:revive
"go.uber.org/mock/gomock"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/util/conditions"

infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
Expand Down Expand Up @@ -548,3 +551,184 @@ func Test_OpenStackServerReconcileCreate(t *testing.T) {
})
}
}

func TestOpenStackServerReconciler_getOrCreateServer(t *testing.T) {
tests := []struct {
name string
openStackServer *infrav1alpha1.OpenStackServer
setupMocks func(r *recorders)
wantServer *servers.Server
wantErr bool
wantCondition *clusterv1.Condition
}{
{
name: "instanceID set in status but server not found",
openStackServer: &infrav1alpha1.OpenStackServer{
Status: infrav1alpha1.OpenStackServerStatus{
InstanceID: ptr.To(instanceUUID),
},
},
setupMocks: func(r *recorders) {
r.compute.GetServer(instanceUUID).Return(nil, gophercloud.ErrUnexpectedResponseCode{Actual: 404})
},
wantErr: false,
wantCondition: &clusterv1.Condition{
Type: infrav1.InstanceReadyCondition,
Status: corev1.ConditionFalse,
Reason: infrav1.InstanceNotFoundReason,
Message: infrav1.ServerUnexpectedDeletedMessage,
},
},
{
name: "instanceID set in status but server not found with error",
openStackServer: &infrav1alpha1.OpenStackServer{
Status: infrav1alpha1.OpenStackServerStatus{
InstanceID: ptr.To(instanceUUID),
},
},
setupMocks: func(r *recorders) {
r.compute.GetServer(instanceUUID).Return(nil, fmt.Errorf("error"))
},
wantErr: true,
wantCondition: &clusterv1.Condition{
Type: infrav1.InstanceReadyCondition,
Status: corev1.ConditionFalse,
Reason: infrav1.OpenStackErrorReason,
Message: "get server \"" + instanceUUID + "\" detail failed: error",
},
},
{
name: "instanceStatus is nil but server found with machine name",
openStackServer: &infrav1alpha1.OpenStackServer{
ObjectMeta: metav1.ObjectMeta{
Name: openStackServerName,
},
Status: infrav1alpha1.OpenStackServerStatus{},
},
setupMocks: func(r *recorders) {
r.compute.ListServers(servers.ListOpts{
Name: "^" + openStackServerName + "$",
}).Return([]servers.Server{{ID: instanceUUID}}, nil)
},
wantErr: false,
wantServer: &servers.Server{
ID: instanceUUID,
},
},
{
name: "instanceStatus is nil and server not found and then created",
openStackServer: &infrav1alpha1.OpenStackServer{
ObjectMeta: metav1.ObjectMeta{
Name: openStackServerName,
},
Status: infrav1alpha1.OpenStackServerStatus{
Resolved: &infrav1alpha1.ResolvedServerSpec{
ImageID: imageUUID,
FlavorID: flavorUUID,
Ports: defaultResolvedPorts,
},
},
},
setupMocks: func(r *recorders) {
r.compute.ListServers(servers.ListOpts{
Name: "^" + openStackServerName + "$",
}).Return([]servers.Server{}, nil)
r.compute.CreateServer(gomock.Any(), gomock.Any()).Return(&servers.Server{ID: instanceUUID}, nil)
},
wantErr: false,
wantServer: &servers.Server{
ID: instanceUUID,
},
// It's off but no condition is set because the server creation was kicked off but we
// don't know the result yet in this function.
},
{
name: "instanceStatus is nil and server not found and then created with error",
openStackServer: &infrav1alpha1.OpenStackServer{
ObjectMeta: metav1.ObjectMeta{
Name: openStackServerName,
},
Status: infrav1alpha1.OpenStackServerStatus{
Resolved: &infrav1alpha1.ResolvedServerSpec{
ImageID: imageUUID,
FlavorID: flavorUUID,
Ports: defaultResolvedPorts,
},
},
},
setupMocks: func(r *recorders) {
r.compute.ListServers(servers.ListOpts{
Name: "^" + openStackServerName + "$",
}).Return([]servers.Server{}, nil)
r.compute.CreateServer(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error"))
},
wantErr: true,
wantCondition: &clusterv1.Condition{
Type: infrav1.InstanceReadyCondition,
Status: corev1.ConditionFalse,
Reason: infrav1.InstanceCreateFailedReason,
Message: "error creating Openstack instance: " + "error",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewGomegaWithT(t)
log := testr.New(t)

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

mockScopeFactory := scope.NewMockScopeFactory(mockCtrl, "")
scopeWithLogger := scope.NewWithLogger(mockScopeFactory, log)

computeRecorder := mockScopeFactory.ComputeClient.EXPECT()
imageRecorder := mockScopeFactory.ImageClient.EXPECT()
networkRecorder := mockScopeFactory.NetworkClient.EXPECT()
volumeRecorder := mockScopeFactory.VolumeClient.EXPECT()

recorders := &recorders{
compute: computeRecorder,
image: imageRecorder,
network: networkRecorder,
volume: volumeRecorder,
}

if tt.setupMocks != nil {
tt.setupMocks(recorders)
}

computeService, err := compute.NewService(scopeWithLogger)
g.Expect(err).ToNot(HaveOccurred())

reconciler := OpenStackServerReconciler{}
status, err := reconciler.getOrCreateServer(ctx, log, tt.openStackServer, computeService, []string{portUUID})

// Check error result
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
} else {
g.Expect(err).ToNot(HaveOccurred())
}

// Check instance status
if tt.wantServer != nil {
g.Expect(status.ID()).To(Equal(tt.wantServer.ID))
}

// Check the condition is set correctly
if tt.wantCondition != nil {
// print openstackServer conditions
for _, condition := range tt.openStackServer.Status.Conditions {
t.Logf("Condition: %s, Status: %s, Reason: %s", condition.Type, condition.Status, condition.Reason)
}
conditionType := conditions.Get(tt.openStackServer, tt.wantCondition.Type)
g.Expect(conditionType).ToNot(BeNil())
g.Expect(conditionType.Status).To(Equal(tt.wantCondition.Status))
g.Expect(conditionType.Reason).To(Equal(tt.wantCondition.Reason))
g.Expect(conditionType.Message).To(Equal(tt.wantCondition.Message))
}
})
}
}
Loading