Skip to content

Commit 94a4126

Browse files
Merge pull request #313 from brendandburns/env
Add config file persistence.
2 parents 72a08ef + 0b6f5df commit 94a4126

File tree

7 files changed

+218
-26
lines changed

7 files changed

+218
-26
lines changed

util/src/main/java/io/kubernetes/client/util/ClientBuilder.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import io.kubernetes.client.util.credentials.KubeconfigAuthentication;
2626
import java.io.ByteArrayInputStream;
2727
import java.io.File;
28-
import java.io.FileNotFoundException;
2928
import java.io.FileReader;
3029
import java.io.IOException;
3130
import java.io.Reader;
@@ -71,13 +70,29 @@ public static ApiClient defaultClient() throws IOException {
7170
* cannot be read.
7271
*/
7372
public static ClientBuilder standard() throws IOException {
74-
final FileReader kubeConfigReader = findConfigFromEnv();
75-
if (kubeConfigReader != null) {
76-
return kubeconfig(loadKubeConfig(kubeConfigReader));
73+
return standard(true);
74+
}
75+
76+
public static ClientBuilder standard(boolean persistConfig) throws IOException {
77+
final File kubeConfig = findConfigFromEnv();
78+
if (kubeConfig != null) {
79+
try (FileReader kubeConfigReader = new FileReader(kubeConfig)) {
80+
KubeConfig kc = loadKubeConfig(kubeConfigReader);
81+
if (persistConfig) {
82+
kc.setPersistConfig(new FilePersister(kubeConfig));
83+
}
84+
return kubeconfig(kc);
85+
}
7786
}
78-
final FileReader configReader = findConfigInHomeDir();
79-
if (configReader != null) {
80-
return kubeconfig(loadKubeConfig(configReader));
87+
final File config = findConfigInHomeDir();
88+
if (config != null) {
89+
try (FileReader configReader = new FileReader(config)) {
90+
KubeConfig kc = loadKubeConfig(configReader);
91+
if (persistConfig) {
92+
kc.setPersistConfig(new FilePersister(config));
93+
}
94+
return kubeconfig(kc);
95+
}
8196
}
8297
final File clusterCa = new File(SERVICEACCOUNT_CA_PATH);
8398
if (clusterCa.exists()) {
@@ -86,24 +101,24 @@ public static ClientBuilder standard() throws IOException {
86101
return new ClientBuilder();
87102
}
88103

89-
private static FileReader findConfigFromEnv() throws FileNotFoundException {
104+
private static File findConfigFromEnv() {
90105
String kubeConfigPath = System.getenv(ENV_KUBECONFIG);
91106
if (kubeConfigPath == null) {
92107
return null;
93108
}
94109
final File kubeConfig = new File(kubeConfigPath);
95110
if (kubeConfig.exists()) {
96-
return new FileReader(kubeConfig);
111+
return kubeConfig;
97112
} else {
98113
log.debug("Could not find file specified in $KUBECONFIG");
99114
return null;
100115
}
101116
}
102117

103-
private static FileReader findConfigInHomeDir() throws FileNotFoundException {
118+
private static File findConfigInHomeDir() {
104119
final File config = new File(new File(System.getenv(ENV_HOME), KUBEDIR), KUBECONFIG);
105120
if (config.exists()) {
106-
return new FileReader(config);
121+
return config;
107122
} else {
108123
log.debug("Could not find ~/.kube/config");
109124
return null;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util;
14+
15+
import java.io.IOException;
16+
import java.util.ArrayList;
17+
18+
public interface ConfigPersister {
19+
public void save(
20+
ArrayList<Object> contexts,
21+
ArrayList<Object> clusters,
22+
ArrayList<Object> users,
23+
Object preferences,
24+
String currentContext)
25+
throws IOException;
26+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util;
14+
15+
import java.io.File;
16+
import java.io.FileWriter;
17+
import java.io.IOException;
18+
import java.util.ArrayList;
19+
import java.util.HashMap;
20+
import org.yaml.snakeyaml.Yaml;
21+
22+
public class FilePersister implements ConfigPersister {
23+
File configFile;
24+
25+
public FilePersister(String filename) {
26+
this(new File(filename));
27+
}
28+
29+
public FilePersister(File file) {
30+
this.configFile = file;
31+
}
32+
33+
public void save(
34+
ArrayList<Object> contexts,
35+
ArrayList<Object> clusters,
36+
ArrayList<Object> users,
37+
Object preferences,
38+
String currentContext)
39+
throws IOException {
40+
HashMap<String, Object> config = new HashMap<>();
41+
config.put("apiVersion", "v1");
42+
config.put("kind", "Config");
43+
config.put("current-context", currentContext);
44+
config.put("preferences", preferences);
45+
46+
config.put("clusters", clusters);
47+
config.put("contexts", contexts);
48+
config.put("users", users);
49+
50+
// Note this is imperfect, should protect against other processes writing this file too...
51+
synchronized (configFile) {
52+
try (FileWriter fw = new FileWriter(configFile)) {
53+
Yaml yaml = new Yaml();
54+
yaml.dump(config, fw);
55+
fw.flush();
56+
}
57+
}
58+
}
59+
}

util/src/main/java/io/kubernetes/client/util/KubeConfig.java

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
import io.kubernetes.client.util.authenticators.Authenticator;
1616
import io.kubernetes.client.util.authenticators.AzureActiveDirectoryAuthenticator;
1717
import io.kubernetes.client.util.authenticators.GCPAuthenticator;
18-
import java.io.File;
19-
import java.io.FileNotFoundException;
20-
import java.io.FileReader;
2118
import java.io.IOException;
2219
import java.io.Reader;
2320
import java.nio.file.FileSystems;
@@ -49,10 +46,13 @@ public class KubeConfig {
4946
private ArrayList<Object> clusters;
5047
private ArrayList<Object> contexts;
5148
private ArrayList<Object> users;
49+
String currentContextName;
5250
Map<String, Object> currentContext;
5351
Map<String, Object> currentCluster;
5452
Map<String, Object> currentUser;
5553
String currentNamespace;
54+
Object preferences;
55+
ConfigPersister persister;
5656

5757
public static void registerAuthenticator(Authenticator auth) {
5858
synchronized (authenticators) {
@@ -65,15 +65,6 @@ public static void registerAuthenticator(Authenticator auth) {
6565
registerAuthenticator(new AzureActiveDirectoryAuthenticator());
6666
}
6767

68-
/** Load a Kubernetes config from the default location */
69-
public static KubeConfig loadDefaultKubeConfig() throws FileNotFoundException {
70-
File config = new File(new File(System.getenv(ENV_HOME), KUBEDIR), KUBECONFIG);
71-
if (!config.exists()) {
72-
return null;
73-
}
74-
return loadKubeConfig(new FileReader(config));
75-
}
76-
7768
/** Load a Kubernetes config from a Reader */
7869
public static KubeConfig loadKubeConfig(Reader input) {
7970
Yaml yaml = new Yaml(new SafeConstructor());
@@ -84,9 +75,11 @@ public static KubeConfig loadKubeConfig(Reader input) {
8475
ArrayList<Object> contexts = (ArrayList<Object>) configMap.get("contexts");
8576
ArrayList<Object> clusters = (ArrayList<Object>) configMap.get("clusters");
8677
ArrayList<Object> users = (ArrayList<Object>) configMap.get("users");
78+
Object preferences = configMap.get("preferences");
8779

8880
KubeConfig kubeConfig = new KubeConfig(contexts, clusters, users);
8981
kubeConfig.setContext(currentContext);
82+
kubeConfig.setPreferences(preferences);
9083

9184
return kubeConfig;
9285
}
@@ -98,11 +91,15 @@ public KubeConfig(
9891
this.users = users;
9992
}
10093

94+
public String getCurrentContext() {
95+
return currentContextName;
96+
}
97+
10198
public boolean setContext(String context) {
10299
if (context == null) {
103100
return false;
104101
}
105-
102+
currentContextName = context;
106103
currentCluster = null;
107104
currentUser = null;
108105
Map<String, Object> ctx = findObject(contexts, context);
@@ -131,10 +128,26 @@ public boolean setContext(String context) {
131128
return true;
132129
}
133130

131+
public ArrayList<Object> getContexts() {
132+
return contexts;
133+
}
134+
135+
public ArrayList<Object> getClusters() {
136+
return clusters;
137+
}
138+
139+
public ArrayList<Object> getUsers() {
140+
return users;
141+
}
142+
134143
public String getNamespace() {
135144
return currentNamespace;
136145
}
137146

147+
public Object getPreferences() {
148+
return preferences;
149+
}
150+
138151
public String getServer() {
139152
return getData(currentCluster, "server");
140153
}
@@ -186,7 +199,13 @@ public String getAccessToken() {
186199
if (auth != null) {
187200
if (auth.isExpired(authConfig)) {
188201
authConfig = auth.refresh(authConfig);
189-
// TODO persist things here.
202+
if (persister != null) {
203+
try {
204+
persister.save(contexts, clusters, users, preferences, currentContextName);
205+
} catch (IOException ex) {
206+
log.error("Failed to persist new token", ex);
207+
}
208+
}
190209
}
191210
return auth.getToken(authConfig);
192211
} else {
@@ -219,6 +238,19 @@ public boolean verifySSL() {
219238
return true;
220239
}
221240

241+
/**
242+
* Set persistence for this config.
243+
*
244+
* @param persister The persistence to use, or null to not persist the config.
245+
*/
246+
public void setPersistConfig(ConfigPersister persister) {
247+
this.persister = persister;
248+
}
249+
250+
public void setPreferences(Object preferences) {
251+
this.preferences = preferences;
252+
}
253+
222254
private static String getData(Map<String, Object> obj, String key) {
223255
if (obj == null) {
224256
return null;

util/src/test/java/io/kubernetes/client/ExecTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class ExecTest {
3737

3838
@Before
3939
public void setup() throws IOException {
40-
client = ClientBuilder.defaultClient();
40+
client = new ClientBuilder().setBasePath("http://localhost:9999").build();
4141
}
4242

4343
@Test
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util;
14+
15+
import static org.junit.Assert.*;
16+
17+
import com.google.common.io.Resources;
18+
import java.io.File;
19+
import java.io.FileReader;
20+
import java.io.IOException;
21+
import org.junit.Rule;
22+
import org.junit.Test;
23+
import org.junit.rules.TemporaryFolder;
24+
25+
/** Tests for the FilePersister helper class */
26+
public class FilePersisterTest {
27+
28+
private static final String KUBECONFIG_FILE_PATH = Resources.getResource("kubeconfig").getPath();
29+
30+
@Rule public TemporaryFolder folder = new TemporaryFolder();
31+
32+
@Test
33+
public void testPersistence() throws IOException {
34+
File file = folder.newFile("testconfig");
35+
FilePersister fp = new FilePersister(file);
36+
37+
KubeConfig config = KubeConfig.loadKubeConfig(new FileReader(KUBECONFIG_FILE_PATH));
38+
39+
fp.save(
40+
config.getContexts(),
41+
config.getClusters(),
42+
config.getUsers(),
43+
config.getPreferences(),
44+
config.getCurrentContext());
45+
46+
KubeConfig configOut = KubeConfig.loadKubeConfig(new FileReader(file));
47+
48+
assertEquals(config.getCurrentContext(), configOut.getCurrentContext());
49+
assertEquals(config.getClusters(), configOut.getClusters());
50+
assertEquals(config.getContexts(), configOut.getContexts());
51+
assertEquals(config.getUsers(), configOut.getUsers());
52+
}
53+
}

util/src/test/resources/kubeconfig-http

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
apiVersion: v1
2+
kind: Config
23
clusters:
34
- cluster:
45
server: localhost
@@ -8,4 +9,10 @@ contexts:
89
- context:
910
cluster: foo
1011
name: foo-context
12+
users:
13+
- user:
14+
client-certificate: /some/fake/path/client.crt
15+
client-key: /some/other/fake/path/client.key
16+
name: fake-user
17+
preferences: {}
1118
current-context: foo-context

0 commit comments

Comments
 (0)