forked from maxlaverse/kubernetes-cli-plugin
/
KubectlBuildStep.java
200 lines (164 loc) · 6.28 KB
/
KubectlBuildStep.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package org.jenkinsci.plugins.kubernetes.cli;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.Item;
import hudson.model.TaskListener;
import hudson.security.ACL;
import hudson.util.ListBoxModel;
import org.jenkinsci.plugins.kubernetes.cli.kubeconfig.KubeConfigWriter;
import org.jenkinsci.plugins.kubernetes.cli.kubeconfig.KubeConfigWriterFactory;
import org.jenkinsci.plugins.kubernetes.credentials.TokenProducer;
import org.jenkinsci.plugins.plaincredentials.FileCredentials;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.jenkinsci.plugins.workflow.steps.*;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Max Laverse
*/
public class KubectlBuildStep extends Step {
@DataBoundSetter
public String serverUrl;
@DataBoundSetter
public String credentialsId;
@DataBoundSetter
public String caCertificate;
@DataBoundSetter
public String contextName;
@DataBoundSetter
public String clusterName;
@DataBoundSetter
public String namespace;
@DataBoundConstructor
public KubectlBuildStep() {
}
@Override
public final StepExecution start(StepContext context) throws Exception {
return new ExecutionImpl(this, context);
}
public static class ExecutionImpl extends AbstractStepExecutionImpl {
private static final long serialVersionUID = 1L;
private transient KubectlBuildStep step;
public ExecutionImpl(KubectlBuildStep step, StepContext context) {
super(context);
this.step = step;
}
/**
* {@inheritDoc}
*/
@Override
public boolean start() throws Exception {
KubeConfigWriter kubeConfigWriter = KubeConfigWriterFactory.get(
step.serverUrl,
step.credentialsId,
step.caCertificate,
step.clusterName,
step.contextName,
step.namespace,
getContext());
// Write config
String configFile = kubeConfigWriter.writeKubeConfig();
// Prepare a new environment
EnvironmentExpander envExpander = EnvironmentExpander.merge(
getContext().get(EnvironmentExpander.class),
new KubeConfigExpander(configFile));
// Execute the commands in the body within this environment
getContext().newBodyInvoker()
.withContext(envExpander)
.withCallback(new Callback(configFile))
.start();
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void stop(@Nonnull Throwable cause) throws Exception {
getContext().onFailure(cause);
}
}
private static final class Callback extends BodyExecutionCallback.TailCall {
private static final long serialVersionUID = 1L;
private final String configFile;
Callback(String configFile) {
this.configFile = configFile;
}
protected void finished(StepContext context) throws Exception {
context.get(FilePath.class).child(configFile).delete();
context.get(TaskListener.class).getLogger().println("kubectl configuration cleaned up");
}
}
@Extension
public static class DescriptorImpl extends StepDescriptor {
// List of supported credentials
private static CredentialsMatcher matcher = CredentialsMatchers.anyOf(
CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class),
CredentialsMatchers.instanceOf(TokenProducer.class),
CredentialsMatchers.instanceOf(StringCredentials.class),
CredentialsMatchers.instanceOf(StandardCertificateCredentials.class),
CredentialsMatchers.instanceOf(FileCredentials.class)
);
/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return "Configure Kubernetes CLI (kubectl)";
}
/**
* {@inheritDoc}
*/
@Override
public String getFunctionName() {
return "withKubeConfig";
}
/**
* {@inheritDoc}
*/
@Override
public boolean takesImplicitBlockArgument() {
return true;
}
@Override
public Set<? extends Class<?>> getRequiredContext() {
return new HashSet<>();
}
public ListBoxModel doFillCredentialsIdItems(@Nonnull @AncestorInPath Item item, @QueryParameter String serverUrl) {
return new StandardListBoxModel()
.includeEmptyValue()
.includeMatchingAs(
ACL.SYSTEM,
item,
StandardCredentials.class,
URIRequirementBuilder.fromUri(serverUrl).build(),
matcher);
}
}
static final class KubeConfigExpander extends EnvironmentExpander {
private final Map<String, String> envOverride;
KubeConfigExpander(String kubeConfigPath) {
this.envOverride = new HashMap<>();
this.envOverride.put(KubeConfigWriter.ENV_VARIABLE_NAME, kubeConfigPath);
}
@Override
public void expand(EnvVars env) throws IOException, InterruptedException {
env.overrideAll(envOverride);
}
}
}