Skip to content

Commit c2dd806

Browse files
committed
feat : Write developer password to file just like kubeadmin password (#2539)
Signed-off-by: Rohan Kumar <[email protected]>
1 parent 130a608 commit c2dd806

13 files changed

+147
-59
lines changed

cmd/crc/cmd/console.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func toConsoleClusterConfig(result *client.ConsoleResult) *clusterConfig {
123123
},
124124
DeveloperCredentials: credentials{
125125
Username: "developer",
126-
Password: "developer",
126+
Password: result.ClusterConfig.DeveloperPass,
127127
},
128128
}
129129
}

cmd/crc/cmd/console_test.go

+21-20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var DummyClusterConfig = types.ClusterConfig{
2020
ClusterCACert: "MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ",
2121
KubeConfig: "/tmp/kubeconfig",
2222
KubeAdminPass: "foobar",
23+
DeveloperPass: "foobar",
2324
ClusterAPI: "https://foo.testing:6443",
2425
WebConsoleURL: "https://console.foo.testing:6443",
2526
ProxyConfig: nil,
@@ -60,42 +61,42 @@ func TestConsolePlainError(t *testing.T) {
6061
}
6162

6263
func TestConsoleWithPrintCredentialsPlainSuccess(t *testing.T) {
63-
expectedOut := fmt.Sprintf(`To login as a regular user, run 'oc login -u developer -p developer %s'.
64+
expectedOut := fmt.Sprintf(`To login as a regular user, run 'oc login -u developer -p %s %s'.
6465
To login as an admin, run 'oc login -u kubeadmin -p %s %s'
65-
`, fakemachine.DummyClusterConfig.ClusterAPI, fakemachine.DummyClusterConfig.KubeAdminPass, fakemachine.DummyClusterConfig.ClusterAPI)
66+
`, fakemachine.DummyClusterConfig.DeveloperPass, fakemachine.DummyClusterConfig.ClusterAPI, fakemachine.DummyClusterConfig.KubeAdminPass, fakemachine.DummyClusterConfig.ClusterAPI)
6667
out := new(bytes.Buffer)
6768
assert.NoError(t, runConsole(out, setUpClientForConsole(t), false, true, ""))
6869
assert.Equal(t, expectedOut, out.String())
6970
}
7071

7172
func TestConsoleWithPrintCredentialsAndURLPlainSuccess(t *testing.T) {
7273
expectedOut := fmt.Sprintf(`%s
73-
To login as a regular user, run 'oc login -u developer -p developer %s'.
74+
To login as a regular user, run 'oc login -u developer -p %s %s'.
7475
To login as an admin, run 'oc login -u kubeadmin -p %s %s'
75-
`, fakemachine.DummyClusterConfig.WebConsoleURL, fakemachine.DummyClusterConfig.ClusterAPI, fakemachine.DummyClusterConfig.KubeAdminPass, fakemachine.DummyClusterConfig.ClusterAPI)
76+
`, fakemachine.DummyClusterConfig.WebConsoleURL, fakemachine.DummyClusterConfig.DeveloperPass, fakemachine.DummyClusterConfig.ClusterAPI, fakemachine.DummyClusterConfig.KubeAdminPass, fakemachine.DummyClusterConfig.ClusterAPI)
7677
out := new(bytes.Buffer)
7778
assert.NoError(t, runConsole(out, setUpClientForConsole(t), true, true, ""))
7879
assert.Equal(t, expectedOut, out.String())
7980
}
8081

8182
func TestConsoleJSONSuccess(t *testing.T) {
8283
expectedJSONOut := fmt.Sprintf(`{
83-
"success": true,
84-
"clusterConfig": {
85-
"clusterType": "openshift",
86-
"cacert": "%s",
87-
"webConsoleUrl": "%s",
88-
"url": "%s",
89-
"adminCredentials": {
90-
"username": "kubeadmin",
91-
"password": "%s"
92-
},
93-
"developerCredentials": {
94-
"username": "developer",
95-
"password": "developer"
96-
}
97-
}
98-
}`, fakemachine.DummyClusterConfig.ClusterCACert, fakemachine.DummyClusterConfig.WebConsoleURL, fakemachine.DummyClusterConfig.ClusterAPI, fakemachine.DummyClusterConfig.KubeAdminPass)
84+
"success": true,
85+
"clusterConfig": {
86+
"clusterType": "openshift",
87+
"cacert": "%s",
88+
"webConsoleUrl": "%s",
89+
"url": "%s",
90+
"adminCredentials": {
91+
"username": "kubeadmin",
92+
"password": "%s"
93+
},
94+
"developerCredentials": {
95+
"username": "developer",
96+
"password": "%s"
97+
}
98+
}
99+
}`, fakemachine.DummyClusterConfig.ClusterCACert, fakemachine.DummyClusterConfig.WebConsoleURL, fakemachine.DummyClusterConfig.ClusterAPI, fakemachine.DummyClusterConfig.KubeAdminPass, fakemachine.DummyClusterConfig.DeveloperPass)
99100
out := new(bytes.Buffer)
100101
assert.NoError(t, runConsole(out, setUpClientForConsole(t), false, false, jsonFormat))
101102
assert.JSONEq(t, expectedJSONOut, out.String())

cmd/crc/cmd/start.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func toClusterConfig(result *types.StartResult) *clusterConfig {
140140
},
141141
DeveloperCredentials: credentials{
142142
Username: "developer",
143-
Password: "developer",
143+
Password: result.ClusterConfig.DeveloperPass,
144144
},
145145
}
146146
}

cmd/crc/cmd/start_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestRenderActionPlainSuccess(t *testing.T) {
3232
},
3333
DeveloperCredentials: credentials{
3434
Username: "developer",
35-
Password: "developer",
35+
Password: "secret",
3636
},
3737
},
3838
}, out, ""))
@@ -118,7 +118,7 @@ Log in as administrator:
118118
119119
Log in as user:
120120
Username: developer
121-
Password: developer
121+
Password: secret
122122
123123
Use the 'oc' command line interface:
124124
$ eval $(crc oc-env)
@@ -136,7 +136,7 @@ Log in as administrator:
136136
137137
Log in as user:
138138
Username: developer
139-
Password: developer
139+
Password: secret
140140
141141
Use the 'oc' command line interface:
142142
PS> & crc oc-env | Invoke-Expression
@@ -154,7 +154,7 @@ Log in as administrator:
154154
155155
Log in as user:
156156
Username: developer
157-
Password: developer
157+
Password: secret
158158
159159
Use the 'oc' command line interface:
160160
> @FOR /f "tokens=*" %i IN ('crc oc-env') DO @call %i

pkg/crc/api/api_client_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func TestStart(t *testing.T) {
9494
ClusterCACert: "MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ",
9595
KubeConfig: "/tmp/kubeconfig",
9696
KubeAdminPass: "foobar",
97+
DeveloperPass: "foobar",
9798
ClusterAPI: "https://foo.testing:6443",
9899
WebConsoleURL: "https://console.foo.testing:6443",
99100
ProxyConfig: nil,

pkg/crc/api/api_http_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ var testCases = []testCase{
167167
// start
168168
{
169169
request: post("start"),
170-
response: jSon(`{"Status":"","ClusterConfig":{"ClusterType":"openshift","ClusterCACert":"MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ","KubeConfig":"/tmp/kubeconfig","KubeAdminPass":"foobar","ClusterAPI":"https://foo.testing:6443","WebConsoleURL":"https://console.foo.testing:6443","ProxyConfig":null},"KubeletStarted":true}`),
170+
response: jSon(`{"Status":"","ClusterConfig":{"ClusterType":"openshift","ClusterCACert":"MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ","KubeConfig":"/tmp/kubeconfig","KubeAdminPass":"foobar","DeveloperPass":"foobar","ClusterAPI":"https://foo.testing:6443","WebConsoleURL":"https://console.foo.testing:6443","ProxyConfig":null},"KubeletStarted":true}`),
171171
},
172172
{
173173
request: get("start"),
174-
response: jSon(`{"Status":"","ClusterConfig":{"ClusterType":"openshift","ClusterCACert":"MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ","KubeConfig":"/tmp/kubeconfig","KubeAdminPass":"foobar","ClusterAPI":"https://foo.testing:6443","WebConsoleURL":"https://console.foo.testing:6443","ProxyConfig":null},"KubeletStarted":true}`),
174+
response: jSon(`{"Status":"","ClusterConfig":{"ClusterType":"openshift","ClusterCACert":"MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ","KubeConfig":"/tmp/kubeconfig","KubeAdminPass":"foobar","DeveloperPass":"foobar","ClusterAPI":"https://foo.testing:6443","WebConsoleURL":"https://console.foo.testing:6443","ProxyConfig":null},"KubeletStarted":true}`),
175175
},
176176

177177
// start with failure
@@ -273,7 +273,7 @@ var testCases = []testCase{
273273
// webconsoleurl
274274
{
275275
request: get("webconsoleurl"),
276-
response: jSon(`{"ClusterConfig":{"ClusterType":"openshift","ClusterCACert":"MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ","KubeConfig":"/tmp/kubeconfig","KubeAdminPass":"foobar","ClusterAPI":"https://foo.testing:6443","WebConsoleURL":"https://console.foo.testing:6443","ProxyConfig":null},"State":"Running"}`),
276+
response: jSon(`{"ClusterConfig":{"ClusterType":"openshift","ClusterCACert":"MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ","KubeConfig":"/tmp/kubeconfig","KubeAdminPass":"foobar","DeveloperPass":"foobar","ClusterAPI":"https://foo.testing:6443","WebConsoleURL":"https://console.foo.testing:6443","ProxyConfig":null},"State":"Running"}`),
277277
},
278278

279279
// webconsoleurl with failure

pkg/crc/cluster/kubeadmin_password.go

+42-27
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,21 @@ import (
1717
"golang.org/x/crypto/bcrypt"
1818
)
1919

20-
// GenerateKubeAdminUserPassword creates and put updated kubeadmin password to ~/.crc/machine/crc/kubeadmin-password
21-
func GenerateKubeAdminUserPassword() error {
22-
logging.Infof("Generating new password for the kubeadmin user")
23-
kubeAdminPasswordFile := constants.GetKubeAdminPasswordPath()
24-
kubeAdminPassword, err := GenerateRandomPasswordHash(23)
20+
// GenerateUserPassword creates and put updated password to ~/.crc/machine/crc/ directory
21+
func GenerateUserPassword(passwordFile string, user string) error {
22+
logging.Infof("Generating new password for the %s user", user)
23+
password, err := GenerateRandomPasswordHash(23)
2524
if err != nil {
26-
return fmt.Errorf("Cannot generate the kubeadmin user password: %w", err)
25+
return fmt.Errorf("cannot generate the %s user password: %w", user, err)
2726
}
28-
return os.WriteFile(kubeAdminPasswordFile, []byte(kubeAdminPassword), 0600)
27+
return os.WriteFile(passwordFile, []byte(password), 0600)
2928
}
3029

31-
// UpdateKubeAdminUserPassword updates the htpasswd secret
32-
func UpdateKubeAdminUserPassword(ctx context.Context, ocConfig oc.Config, newPassword string) error {
33-
if newPassword != "" {
34-
logging.Infof("Overriding password for kubeadmin user")
35-
if err := os.WriteFile(constants.GetKubeAdminPasswordPath(), []byte(strings.TrimSpace(newPassword)), 0600); err != nil {
36-
return err
37-
}
38-
}
39-
40-
kubeAdminPassword, err := GetKubeadminPassword()
30+
// UpdateUserPasswords updates the htpasswd secret
31+
func UpdateUserPasswords(ctx context.Context, ocConfig oc.Config, newKubeAdminPassword string, newDeveloperPassword string) error {
32+
credentials, err := resolveUserPasswords(newKubeAdminPassword, newDeveloperPassword, constants.GetKubeAdminPasswordPath(), constants.GetDeveloperPasswordPath())
4133
if err != nil {
42-
return fmt.Errorf("Cannot read the kubeadmin user password from file: %w", err)
43-
}
44-
credentials := map[string]string{
45-
"developer": "developer",
46-
"kubeadmin": kubeAdminPassword,
34+
return err
4735
}
4836

4937
if err := WaitForOpenshiftResource(ctx, ocConfig, "secret"); err != nil {
@@ -62,7 +50,7 @@ func UpdateKubeAdminUserPassword(ctx context.Context, ocConfig oc.Config, newPas
6250
return nil
6351
}
6452

65-
logging.Infof("Changing the password for the kubeadmin user")
53+
logging.Infof("Changing the password for the users")
6654
expected, err := getHtpasswd(credentials, externals)
6755
if err != nil {
6856
return err
@@ -72,14 +60,13 @@ func UpdateKubeAdminUserPassword(ctx context.Context, ocConfig oc.Config, newPas
7260
"-n", "openshift-config", "--type", "merge"}
7361
_, stderr, err = ocConfig.RunOcCommandPrivate(cmdArgs...)
7462
if err != nil {
75-
return fmt.Errorf("Failed to update kubeadmin password %v: %s", err, stderr)
63+
return fmt.Errorf("failed to update user passwords %v: %s", err, stderr)
7664
}
7765
return nil
7866
}
7967

80-
func GetKubeadminPassword() (string, error) {
81-
kubeAdminPasswordFile := constants.GetKubeAdminPasswordPath()
82-
rawData, err := os.ReadFile(kubeAdminPasswordFile)
68+
func GetUserPassword(passwordFile string) (string, error) {
69+
rawData, err := os.ReadFile(passwordFile)
8370
if err != nil {
8471
return "", err
8572
}
@@ -192,3 +179,31 @@ func testBCryptPassword(password, hash string) (bool, error) {
192179
}
193180
return true, nil
194181
}
182+
183+
func resolveUserPasswords(newKubeAdminPassword string, newDeveloperPassword string, kubeAdminPasswordPath string, developerPasswordPath string) (map[string]string, error) {
184+
if newKubeAdminPassword != "" {
185+
logging.Infof("Overriding password for kubeadmin user")
186+
if err := os.WriteFile(kubeAdminPasswordPath, []byte(strings.TrimSpace(newKubeAdminPassword)), 0600); err != nil {
187+
return nil, err
188+
}
189+
}
190+
if newDeveloperPassword != "" {
191+
logging.Infof("Overriding password for developer user")
192+
if err := os.WriteFile(developerPasswordPath, []byte(strings.TrimSpace(newDeveloperPassword)), 0600); err != nil {
193+
return nil, err
194+
}
195+
}
196+
197+
kubeAdminPassword, err := GetUserPassword(kubeAdminPasswordPath)
198+
if err != nil {
199+
return nil, fmt.Errorf("cannot read the kubeadmin user password from file: %w", err)
200+
}
201+
developerPassword, err := GetUserPassword(developerPasswordPath)
202+
if err != nil {
203+
return nil, fmt.Errorf("cannot read the developer user password from file: %w", err)
204+
}
205+
return map[string]string{
206+
"developer": developerPassword,
207+
"kubeadmin": kubeAdminPassword,
208+
}, nil
209+
}

pkg/crc/cluster/kubeadmin_password_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cluster
22

33
import (
4+
"os"
5+
"path/filepath"
46
"testing"
57

68
"github.com/stretchr/testify/assert"
@@ -56,3 +58,58 @@ func TestCompareFalseWithCustomEntries(t *testing.T) {
5658
assert.NoError(t, err)
5759
assert.True(t, ok)
5860
}
61+
62+
func TestGenerateUserPassword_WhenValidFileProvided_ThenWritePasswordToFile(t *testing.T) {
63+
// Given
64+
dir := t.TempDir()
65+
userPasswordPath := filepath.Join(dir, "test-user-password")
66+
// When
67+
err := GenerateUserPassword(userPasswordPath, "test-user")
68+
// Then
69+
assert.NoError(t, err)
70+
actualPasswordFileContents, err := os.ReadFile(userPasswordPath)
71+
assert.NoError(t, err)
72+
assert.Equal(t, 23, len(actualPasswordFileContents))
73+
}
74+
75+
var testResolveUserPasswordArguments = map[string]struct {
76+
kubeAdminPasswordViaConfig string
77+
developerPasswordViaConfig string
78+
expectedKubeAdminPassword string
79+
expectedDeveloperPassword string
80+
}{
81+
"When no password configured in config, then read kubeadmin and developer passwords from password files": {
82+
"", "", "kubeadmin-password-via-file", "developer-password-via-file",
83+
},
84+
"When developer password configured in config, then use developer password from config": {
85+
"", "developer-password-via-config", "kubeadmin-password-via-file", "developer-password-via-config",
86+
},
87+
"When kube admin password configured in config, then use kube admin password from config": {
88+
"kubeadmin-password-via-config", "", "kubeadmin-password-via-config", "developer-password-via-file",
89+
},
90+
"When kube admin and developer password configured in config, then use kube admin and developer passwords from config": {
91+
"kubeadmin-password-via-config", "developer-password-via-config", "kubeadmin-password-via-config", "developer-password-via-config",
92+
},
93+
}
94+
95+
func TestResolveUserPassword_WhenNothingProvided_ThenUsePasswordFromFiles(t *testing.T) {
96+
for name, test := range testResolveUserPasswordArguments {
97+
t.Run(name, func(t *testing.T) {
98+
// Given
99+
dir := t.TempDir()
100+
kubeAdminPasswordPath := filepath.Join(dir, "kubeadmin-password")
101+
err := os.WriteFile(kubeAdminPasswordPath, []byte("kubeadmin-password-via-file"), 0600)
102+
assert.NoError(t, err)
103+
developerPasswordPath := filepath.Join(dir, "developer-password")
104+
err = os.WriteFile(developerPasswordPath, []byte("developer-password-via-file"), 0600)
105+
assert.NoError(t, err)
106+
107+
// When
108+
credentials, err := resolveUserPasswords(test.kubeAdminPasswordViaConfig, test.developerPasswordViaConfig, kubeAdminPasswordPath, developerPasswordPath)
109+
110+
// Then
111+
assert.NoError(t, err)
112+
assert.Equal(t, map[string]string{"developer": test.expectedDeveloperPassword, "kubeadmin": test.expectedKubeAdminPassword}, credentials)
113+
})
114+
}
115+
}

pkg/crc/constants/constants.go

+4
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ func GetKubeAdminPasswordPath() string {
194194
return filepath.Join(MachineInstanceDir, DefaultName, "kubeadmin-password")
195195
}
196196

197+
func GetDeveloperPasswordPath() string {
198+
return filepath.Join(MachineInstanceDir, DefaultName, "developer-password")
199+
}
200+
197201
func GetWin32BackgroundLauncherDownloadURL() string {
198202
return fmt.Sprintf(BackgroundLauncherURL,
199203
version.GetWin32BackgroundLauncherVersion())

pkg/crc/machine/fakemachine/client.go

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var DummyClusterConfig = types.ClusterConfig{
2929
ClusterCACert: "MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ",
3030
KubeConfig: "/tmp/kubeconfig",
3131
KubeAdminPass: "foobar",
32+
DeveloperPass: "foobar",
3233
ClusterAPI: "https://foo.testing:6443",
3334
WebConsoleURL: "https://console.foo.testing:6443",
3435
ProxyConfig: nil,

pkg/crc/machine/machine.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ func getClusterConfig(bundleInfo *bundle.CrcBundleInfo) (*types.ClusterConfig, e
2121
}, nil
2222
}
2323

24-
kubeadminPassword, err := cluster.GetKubeadminPassword()
24+
kubeadminPassword, err := cluster.GetUserPassword(constants.GetKubeAdminPasswordPath())
2525
if err != nil {
2626
return nil, fmt.Errorf("Error reading kubeadmin password from bundle %v", err)
2727
}
28+
developerPassword, err := cluster.GetUserPassword(constants.GetDeveloperPasswordPath())
29+
if err != nil {
30+
return nil, fmt.Errorf("error reading developer password from bundle %v", err)
31+
}
2832
proxyConfig, err := getProxyConfig(bundleInfo)
2933
if err != nil {
3034
return nil, err
@@ -38,6 +42,7 @@ func getClusterConfig(bundleInfo *bundle.CrcBundleInfo) (*types.ClusterConfig, e
3842
ClusterCACert: base64.StdEncoding.EncodeToString(clusterCACert),
3943
KubeConfig: bundleInfo.GetKubeConfigPath(),
4044
KubeAdminPass: kubeadminPassword,
45+
DeveloperPass: developerPassword,
4146
WebConsoleURL: fmt.Sprintf("https://%s", bundleInfo.GetAppHostname("console-openshift-console")),
4247
ClusterAPI: fmt.Sprintf("https://%s:6443", bundleInfo.GetAPIHostname()),
4348
ProxyConfig: proxyConfig,

pkg/crc/machine/start.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
579579
return nil, errors.Wrap(err, "Failed to update pull secret on the disk")
580580
}
581581

582-
if err := cluster.UpdateKubeAdminUserPassword(ctx, ocConfig, startConfig.KubeAdminPassword); err != nil {
582+
if err := cluster.UpdateUserPasswords(ctx, ocConfig, startConfig.KubeAdminPassword, startConfig.DeveloperPassword); err != nil {
583583
return nil, errors.Wrap(err, "Failed to update kubeadmin user password")
584584
}
585585

@@ -686,9 +686,12 @@ func createHost(machineConfig config.MachineConfig, preset crcPreset.Preset) err
686686
return fmt.Errorf("Error generating ssh key pair: %v", err)
687687
}
688688
if preset == crcPreset.OpenShift || preset == crcPreset.OKD {
689-
if err := cluster.GenerateKubeAdminUserPassword(); err != nil {
689+
if err := cluster.GenerateUserPassword(constants.GetKubeAdminPasswordPath(), "kubeadmin"); err != nil {
690690
return errors.Wrap(err, "Error generating new kubeadmin password")
691691
}
692+
if err = os.WriteFile(constants.GetDeveloperPasswordPath(), []byte(constants.DefaultDeveloperPassword), 0600); err != nil {
693+
return errors.Wrap(err, "Error writing developer password")
694+
}
692695
}
693696
if err := api.SetExists(vm.Name); err != nil {
694697
return fmt.Errorf("Failed to record VM existence: %s", err)

0 commit comments

Comments
 (0)