Skip to content

Commit 0342835

Browse files
rohanKanojiapraveenkumar
authored andcommitted
fix (crc/machine) : KubeContext left in invalid state after crc stop
At the moment, we are only cleaning up crc context from kubeconfig during `crc delete`. This can be problematic if user tries to run any cluster related command after running `crc stop` as kubeconfig still points to CRC cluster that is not active. I checked minikube's behavior and noticed it's cleaning up kube config in case of both stop and delete commands. Make crc behavior consistent with minikube and perform kubeconfig cleanup in both sub commands. Signed-off-by: Rohan Kumar <[email protected]>
1 parent 268795f commit 0342835

File tree

7 files changed

+155
-2
lines changed

7 files changed

+155
-2
lines changed

pkg/crc/machine/kubeconfig.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func updateClientCrtAndKeyToKubeconfig(clientKey, clientCrt []byte, srcKubeconfi
5050
}
5151

5252
func writeKubeconfig(ip string, clusterConfig *types.ClusterConfig, ingressHTTPSPort uint) error {
53-
kubeconfig, cfg, err := getGlobalKubeConfig()
53+
kubeconfig, cfg, err := GetGlobalKubeConfig()
5454
if err != nil {
5555
return err
5656
}
@@ -91,7 +91,7 @@ func writeKubeconfig(ip string, clusterConfig *types.ClusterConfig, ingressHTTPS
9191
return clientcmd.WriteToFile(*cfg, kubeconfig)
9292
}
9393

94-
func getGlobalKubeConfig() (string, *api.Config, error) {
94+
func GetGlobalKubeConfig() (string, *api.Config, error) {
9595
kubeconfig := getGlobalKubeConfigPath()
9696
return getKubeConfigFromFile(kubeconfig)
9797
}

pkg/crc/machine/kubeconfig_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,32 @@ func TestCleanKubeconfig(t *testing.T) {
5252
assert.YAMLEq(t, string(expected), string(actual))
5353
}
5454

55+
func TestCleanKubeConfigIdempotency(t *testing.T) {
56+
// Given
57+
dir := t.TempDir()
58+
// When
59+
assert.NoError(t, cleanKubeconfig(filepath.Join("testdata", "kubeconfig.out"), filepath.Join(dir, "kubeconfig")))
60+
actual, err := os.ReadFile(filepath.Join(dir, "kubeconfig"))
61+
// Then
62+
assert.NoError(t, err)
63+
expected, err := os.ReadFile(filepath.Join("testdata", "kubeconfig.out"))
64+
assert.NoError(t, err)
65+
assert.YAMLEq(t, string(expected), string(actual))
66+
}
67+
68+
func TestCleanKubeConfigShouldDoNothingWhenClusterDomainIsNotEqualToCrcTesting(t *testing.T) {
69+
// Given
70+
dir := t.TempDir()
71+
// When
72+
assert.NoError(t, cleanKubeconfig(filepath.Join("testdata", "kubeconfig-without-api-crc-testing-cluster-domain"), filepath.Join(dir, "kubeconfig")))
73+
actual, err := os.ReadFile(filepath.Join(dir, "kubeconfig"))
74+
// Then
75+
assert.NoError(t, err)
76+
expected, err := os.ReadFile(filepath.Join("testdata", "kubeconfig-without-api-crc-testing-cluster-domain"))
77+
assert.NoError(t, err)
78+
assert.YAMLEq(t, string(expected), string(actual))
79+
}
80+
5581
func TestUpdateUserCaAndKeyToKubeconfig(t *testing.T) {
5682
f, err := os.CreateTemp("", "kubeconfig")
5783
assert.NoError(t, err, "")

pkg/crc/machine/stop.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package machine
22

33
import (
4+
"os"
5+
46
"github.com/crc-org/crc/v2/pkg/crc/logging"
57
"github.com/crc-org/crc/v2/pkg/crc/machine/state"
68
crcPreset "github.com/crc-org/crc/v2/pkg/crc/preset"
@@ -9,6 +11,12 @@ import (
911
)
1012

1113
func (client *client) Stop() (state.State, error) {
14+
defer func(input, output string) {
15+
err := cleanKubeconfig(input, output)
16+
if !errors.Is(err, os.ErrNotExist) {
17+
logging.Warnf("Failed to remove crc contexts from kubeconfig: %v", err)
18+
}
19+
}(getGlobalKubeConfigPath(), getGlobalKubeConfigPath())
1220
if running, _ := client.IsRunning(); !running {
1321
return state.Error, errors.New("Instance is already stopped")
1422
}

pkg/crc/machine/stop_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package machine
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
crcConfig "github.com/crc-org/crc/v2/pkg/crc/config"
9+
"github.com/crc-org/crc/v2/pkg/crc/machine/state"
10+
crcOs "github.com/crc-org/crc/v2/pkg/os"
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func TestClient_WhenStopInvokedWithNonExistentVM_ThenThrowError(t *testing.T) {
15+
// Given
16+
dir := t.TempDir()
17+
oldKubeConfigEnvVarValue := os.Getenv("KUBECONFIG")
18+
kubeConfigPath := filepath.Join(dir, "kubeconfig")
19+
err := crcOs.CopyFile(filepath.Join("testdata", "kubeconfig.in"), kubeConfigPath)
20+
assert.NoError(t, err)
21+
err = os.Setenv("KUBECONFIG", kubeConfigPath)
22+
assert.NoError(t, err)
23+
crcConfigStorage := crcConfig.New(crcConfig.NewEmptyInMemoryStorage(), crcConfig.NewEmptyInMemorySecretStorage())
24+
client := NewClient("i-dont-exist", false, crcConfigStorage)
25+
26+
// When
27+
clusterState, stopErr := client.Stop()
28+
29+
// Then
30+
assert.EqualError(t, stopErr, "Instance is already stopped")
31+
assert.Equal(t, clusterState, state.Error)
32+
err = os.Setenv("KUBECONFIG", oldKubeConfigEnvVarValue)
33+
assert.NoError(t, err)
34+
}
35+
36+
var testArguments = map[string]struct {
37+
inputKubeConfigPath string
38+
expectedKubeConfigPath string
39+
}{
40+
"When KubeConfig contains crc context, then cleanup KubeConfig": {
41+
"kubeconfig.in", "kubeconfig.out",
42+
},
43+
"When KubeConfig does not contain crc context, then KubeConfig remains unchanged": {
44+
"kubeconfig.out", "kubeconfig.out",
45+
},
46+
}
47+
48+
func TestClient_WhenStopInvoked_ThenKubeConfigUpdatedIfRequired(t *testing.T) {
49+
for name, test := range testArguments {
50+
t.Run(name, func(t *testing.T) {
51+
// Given
52+
dir := t.TempDir()
53+
oldKubeConfigEnvVarValue := os.Getenv("KUBECONFIG")
54+
kubeConfigPath := filepath.Join(dir, "kubeconfig")
55+
err := crcOs.CopyFile(filepath.Join("testdata", test.inputKubeConfigPath), kubeConfigPath)
56+
assert.NoError(t, err)
57+
err = os.Setenv("KUBECONFIG", kubeConfigPath)
58+
assert.NoError(t, err)
59+
crcConfigStorage := crcConfig.New(crcConfig.NewEmptyInMemoryStorage(), crcConfig.NewEmptyInMemorySecretStorage())
60+
client := NewClient("test-client", false, crcConfigStorage)
61+
62+
// When
63+
clusterState, _ := client.Stop()
64+
65+
// Then
66+
actualKubeConfigFile, err := os.ReadFile(kubeConfigPath)
67+
assert.NoError(t, err)
68+
expectedKubeConfigPath, err := os.ReadFile(filepath.Join("testdata", test.expectedKubeConfigPath))
69+
assert.NoError(t, err)
70+
assert.YAMLEq(t, string(expectedKubeConfigPath), string(actualKubeConfigFile))
71+
assert.Equal(t, clusterState, state.Error)
72+
err = os.Setenv("KUBECONFIG", oldKubeConfigEnvVarValue)
73+
assert.NoError(t, err)
74+
})
75+
}
76+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: v1
2+
clusters:
3+
- cluster:
4+
server: https://api.unknown.testing:6443
5+
name: api-crc-testing:6443
6+
contexts:
7+
- context:
8+
cluster: api-crc-testing:6443
9+
namespace: default
10+
user: kubeadmin/api-crc-testing:6443
11+
name: default/api-crc-testing:6443/kubeadmin
12+
current-context: default/api-crc-testing:6443/kubeadmin
13+
kind: Config
14+
preferences: {}
15+
users:
16+
- name: kubeadmin/api-crc-testing:6443
17+
user:
18+
token: sha256~secret

test/e2e/features/basic.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Feature: Basic test
6060
When executing "crc stop"
6161
Then stdout should match "(.*)[Ss]topped the instance"
6262
And executing "oc whoami" fails
63+
And kubeconfig is cleaned up
6364
# status check
6465
When checking that CRC is stopped
6566
And stdout should not contain "Running"
@@ -69,5 +70,6 @@ Feature: Basic test
6970
# delete
7071
When executing "crc delete -f" succeeds
7172
Then stdout should contain "Deleted the instance"
73+
And kubeconfig is cleaned up
7274
# cleanup
7375
When executing crc cleanup command succeeds

test/e2e/testsuite/testsuite.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
"github.com/crc-org/crc/v2/pkg/crc/constants"
16+
"github.com/crc-org/crc/v2/pkg/crc/machine"
1617
"github.com/crc-org/crc/v2/pkg/crc/preset"
1718
"github.com/crc-org/crc/v2/pkg/crc/version"
1819
"github.com/crc-org/crc/v2/test/extended/crc/cmd"
@@ -522,6 +523,8 @@ func InitializeScenario(s *godog.ScenarioContext) {
522523
EnsureUserNetworkmode)
523524
s.Step(`^ensuring microshift cluster is fully operational$`,
524525
EnsureMicroshiftClusterIsOperational)
526+
s.Step(`^kubeconfig is cleaned up$`,
527+
EnsureKubeConfigIsCleanedUp)
525528

526529
s.After(func(ctx context.Context, _ *godog.Scenario, err error) (context.Context, error) {
527530

@@ -1025,6 +1028,26 @@ func EnsureUserNetworkmode() error {
10251028
return nil
10261029
}
10271030

1031+
func EnsureKubeConfigIsCleanedUp() error {
1032+
kubeConfig, cfg, err := machine.GetGlobalKubeConfig()
1033+
if err != nil {
1034+
return err
1035+
}
1036+
if len(kubeConfig) == 0 {
1037+
return fmt.Errorf("unable to load kube config file while verifying kube config cleanup")
1038+
}
1039+
if cfg.CurrentContext != "" {
1040+
return fmt.Errorf("kube config's current context not cleaned up. [expected : \"\", actual : %s]", cfg.CurrentContext)
1041+
}
1042+
crcClusterDomain := fmt.Sprintf("https://api%s:6443", constants.ClusterDomain)
1043+
for name, cluster := range cfg.Clusters {
1044+
if cluster.Server == crcClusterDomain {
1045+
return fmt.Errorf("kube config's cluster %s is not cleaned up, it still contains a cluster with %s domain [expected : \"\", actual : %s]", name, crcClusterDomain, crcClusterDomain)
1046+
}
1047+
}
1048+
return nil
1049+
}
1050+
10281051
// This function will wait until the microshift cluster got operational
10291052
func EnsureMicroshiftClusterIsOperational() error {
10301053
// First wait until crc report the cluster as running

0 commit comments

Comments
 (0)