Skip to content

Commit 0f62a94

Browse files
authored
Merge pull request #166 from jglick/FilePathPickle-JENKINS-75679
[JENKINS-75679] `KeyMaterial` variant passing `VirtualChannel` to `close`
2 parents 06e46a5 + c7f1ea5 commit 0f62a94

File tree

13 files changed

+330
-63
lines changed

13 files changed

+330
-63
lines changed

src/main/java/org/jenkinsci/plugins/docker/commons/credentials/KeyMaterial.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
package org.jenkinsci.plugins.docker.commons.credentials;
2525

2626
import hudson.EnvVars;
27+
import hudson.FilePath;
28+
import hudson.remoting.VirtualChannel;
2729

2830
import java.io.Closeable;
2931
import java.io.IOException;
@@ -40,7 +42,9 @@
4042
* @author Kohsuke Kawaguchi
4143
* @see DockerServerEndpoint#newKeyMaterialFactory(hudson.model.AbstractBuild)
4244
* @see DockerRegistryEndpoint#newKeyMaterialFactory(hudson.model.AbstractBuild)
45+
* @deprecated use {@link KeyMaterial2}
4346
*/
47+
@Deprecated
4448
public abstract class KeyMaterial implements Closeable, Serializable {
4549

4650
/**
@@ -88,4 +92,41 @@ private Object readResolve() {
8892
return NULL;
8993
}
9094
}
95+
96+
static KeyMaterial fromKeyMaterial2(KeyMaterial2 material, FilePath baseDir) {
97+
return new KeyMaterialFromKeyMaterial2(material, baseDir);
98+
}
99+
private static final class KeyMaterialFromKeyMaterial2 extends KeyMaterial {
100+
private static final long serialVersionUID = 1L;
101+
private final KeyMaterial2 delegate;
102+
private final FilePath baseDir;
103+
KeyMaterialFromKeyMaterial2(KeyMaterial2 delegate, FilePath baseDir) {
104+
super(delegate.env());
105+
this.delegate = delegate;
106+
this.baseDir = baseDir;
107+
}
108+
@Override public void close() throws IOException {
109+
try {
110+
delegate.close(baseDir != null ? baseDir.getChannel() : FilePath.localChannel);
111+
} catch (InterruptedException x) {
112+
throw new IOException(x);
113+
}
114+
}
115+
}
116+
117+
118+
KeyMaterial2 toKeyMaterial2() {
119+
return new KeyMaterial2FromKeyMaterial(this);
120+
}
121+
private static final class KeyMaterial2FromKeyMaterial extends KeyMaterial2 {
122+
private static final long serialVersionUID = 1L;
123+
private final KeyMaterial delegate;
124+
KeyMaterial2FromKeyMaterial(KeyMaterial delegate) {
125+
super(delegate.env());
126+
this.delegate = delegate;
127+
}
128+
@Override public void close(VirtualChannel channel) throws IOException {
129+
delegate.close();
130+
}
131+
}
91132
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2015, CloudBees, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
package org.jenkinsci.plugins.docker.commons.credentials;
25+
26+
import hudson.EnvVars;
27+
import hudson.remoting.VirtualChannel;
28+
29+
import java.io.IOException;
30+
import java.io.Serializable;
31+
32+
/**
33+
* Represents a locally extracted credentials information.
34+
*
35+
* <p>
36+
* Implementations of this class are created by their corresponding {@link KeyMaterialFactory}
37+
* implementations. Be sure to call {@link #close} when finished.
38+
*
39+
* @author Kohsuke Kawaguchi
40+
*/
41+
public abstract class KeyMaterial2 implements Serializable {
42+
43+
/**
44+
* Standardize serialization
45+
*/
46+
private static final long serialVersionUID = 1L;
47+
48+
/**
49+
* {@link KeyMaterial} that does nothing.
50+
*/
51+
public static final KeyMaterial2 NULL = new NullKeyMaterial();
52+
53+
/**
54+
* The environment variables
55+
*/
56+
private final EnvVars envVars;
57+
58+
protected KeyMaterial2(EnvVars envVars) {
59+
this.envVars = envVars;
60+
}
61+
62+
/**
63+
* Get the environment variables needed to be passed when docker runs, to access
64+
* {@link DockerServerCredentials} that this object was created from.
65+
*/
66+
public final EnvVars env() {
67+
return envVars;
68+
}
69+
70+
/**
71+
* Deletes the key materials from the file system. As key materials are copied into files
72+
* every time {@link KeyMaterialFactory} is created, it must be also cleaned up each time.
73+
* @param channel to restore pathnames
74+
*/
75+
public abstract void close(VirtualChannel channel) throws IOException, InterruptedException;
76+
77+
private static final class NullKeyMaterial extends KeyMaterial2 {
78+
private static final long serialVersionUID = 1L;
79+
protected NullKeyMaterial() {
80+
super(new EnvVars());
81+
}
82+
@Override
83+
public void close(VirtualChannel channel) {
84+
}
85+
private Object readResolve() {
86+
return NULL;
87+
}
88+
}
89+
}

src/main/java/org/jenkinsci/plugins/docker/commons/credentials/KeyMaterialContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
*
3535
* @author Stephen Connolly
3636
*/
37+
// TODO why is this marked Serializable? It does not in fact seem to be serialized.
3738
public class KeyMaterialContext implements Serializable {
3839
private static final long serialVersionUID = 1L;
3940

src/main/java/org/jenkinsci/plugins/docker/commons/credentials/KeyMaterialFactory.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,19 @@
2323
*/
2424
package org.jenkinsci.plugins.docker.commons.credentials;
2525

26+
import edu.umd.cs.findbugs.annotations.CheckForNull;
2627
import edu.umd.cs.findbugs.annotations.NonNull;
2728
import edu.umd.cs.findbugs.annotations.Nullable;
2829
import hudson.FilePath;
30+
import hudson.Util;
2931
import hudson.model.AbstractBuild;
30-
import org.jenkinsci.plugins.docker.commons.impl.CompositeKeyMaterialFactory;
31-
import org.jenkinsci.plugins.docker.commons.impl.NullKeyMaterialFactory;
32-
32+
import hudson.remoting.VirtualChannel;
3333
import java.io.IOException;
3434
import java.util.ArrayList;
3535
import java.util.List;
3636
import java.util.UUID;
37+
import org.jenkinsci.plugins.docker.commons.impl.CompositeKeyMaterialFactory;
38+
import org.jenkinsci.plugins.docker.commons.impl.NullKeyMaterialFactory;
3739

3840
/**
3941
* Represents a locally extracted credentials information.
@@ -78,15 +80,40 @@ protected synchronized KeyMaterialContext getContext() {
7880
return context;
7981
}
8082

83+
@NonNull
84+
protected final synchronized VirtualChannel getChannel() {
85+
return context != null ? context.getBaseDir().getChannel() : FilePath.localChannel;
86+
}
87+
88+
/** @deprecated use {@link #materialize2} */
89+
@Deprecated
90+
public KeyMaterial materialize() throws IOException, InterruptedException {
91+
if (Util.isOverridden(KeyMaterialFactory.class, getClass(), "materialize2")) {
92+
FilePath baseDir;
93+
synchronized (this) {
94+
baseDir = context != null ? context.getBaseDir() : null;
95+
}
96+
return KeyMaterial.fromKeyMaterial2(materialize2(), baseDir);
97+
} else {
98+
throw new AbstractMethodError("Override KeyMaterialFactory.materialize2 from " + getClass());
99+
}
100+
}
101+
81102
/**
82103
* Builds the key material environment variables needed to be passed when docker runs, to access
83104
* {@link DockerServerCredentials} that this object was created from.
84-
*
105+
*
85106
* <p>
86-
* When you are done using the credentials, call {@link KeyMaterial#close()} to allow sensitive
107+
* When you are done using the credentials, call {@link KeyMaterial2#close} to allow sensitive
87108
* information to be removed from the disk.
88109
*/
89-
public abstract KeyMaterial materialize() throws IOException, InterruptedException;
110+
public KeyMaterial2 materialize2() throws IOException, InterruptedException {
111+
if (Util.isOverridden(KeyMaterialFactory.class, getClass(), "materialize")) {
112+
return materialize().toKeyMaterial2();
113+
} else {
114+
throw new AbstractMethodError("Override KeyMaterialFactory.materialize2 from " + getClass());
115+
}
116+
}
90117

91118
/**
92119
* Creates a read-protected directory inside {@link KeyMaterialContext#getBaseDir} suitable for storing secret files.

src/main/java/org/jenkinsci/plugins/docker/commons/impl/CompositeKeyMaterialFactory.java

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
*/
2424
package org.jenkinsci.plugins.docker.commons.impl;
2525

26+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2627
import hudson.EnvVars;
28+
import hudson.remoting.VirtualChannel;
2729
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial;
30+
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial2;
2831
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialContext;
2932
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialFactory;
3033
import org.kohsuke.accmod.Restricted;
@@ -60,52 +63,92 @@ public synchronized KeyMaterialFactory contextualize(@NonNull KeyMaterialContext
6063
}
6164

6265
@Override
63-
public KeyMaterial materialize() throws IOException, InterruptedException {
66+
public KeyMaterial2 materialize2() throws IOException, InterruptedException {
6467

65-
KeyMaterial[] keyMaterials = new KeyMaterial[factories.length];
68+
KeyMaterial2[] keyMaterials = new KeyMaterial2[factories.length];
6669
EnvVars env = new EnvVars();
6770
try {
6871
for (int index = 0; index < factories.length; index++) {
69-
keyMaterials[index] = factories[index].materialize();
72+
keyMaterials[index] = factories[index].materialize2();
7073
env.putAll(keyMaterials[index].env());
7174
}
72-
return new CompositeKeyMaterial(env, keyMaterials);
75+
return new CompositeKeyMaterial2(env, keyMaterials);
7376
} catch (Throwable e) {
7477
for (int index = keyMaterials.length - 1; index >= 0; index--) {
7578
try {
7679
if (keyMaterials[index] != null) {
77-
keyMaterials[index].close();
80+
keyMaterials[index].close(getChannel());
7881
}
79-
} catch (IOException ioe) {
80-
// ignore as we want to try and close them all and we are reporting the original exception
8182
} catch (Throwable t) {
82-
// ignore as we want to try and close them all and we are reporting the original exception
83+
e.addSuppressed(t);
8384
}
8485
}
85-
// TODO Java 7+ use chained exceptions
86-
if (e instanceof IOException) {
87-
throw (IOException) e;
88-
} else if (e instanceof InterruptedException) {
89-
throw (InterruptedException) e;
90-
} else if (e instanceof RuntimeException) {
91-
throw (RuntimeException) e;
86+
if (e instanceof IOException ioe) {
87+
throw ioe;
88+
} else if (e instanceof InterruptedException ie) {
89+
throw ie;
90+
} else if (e instanceof RuntimeException re) {
91+
throw re;
9292
} else {
9393
throw new IOException("Error materializing credentials.", e);
9494
}
9595
}
9696
}
9797

98-
private static final class CompositeKeyMaterial extends KeyMaterial implements Serializable {
98+
private static final class CompositeKeyMaterial2 extends KeyMaterial2 implements Serializable {
9999

100100
private static final long serialVersionUID = 1L;
101101

102-
private final KeyMaterial[] keyMaterials;
102+
private final KeyMaterial2[] keyMaterials;
103103

104-
protected CompositeKeyMaterial(EnvVars envVars, KeyMaterial... keyMaterials) {
104+
CompositeKeyMaterial2(EnvVars envVars, KeyMaterial2... keyMaterials) {
105105
super(envVars);
106106
this.keyMaterials = keyMaterials;
107107
}
108108

109+
@Override
110+
public void close(VirtualChannel channel) throws IOException, InterruptedException {
111+
Throwable first = null;
112+
for (int index = keyMaterials.length - 1; index >= 0; index--) {
113+
try {
114+
if (keyMaterials[index] != null) {
115+
keyMaterials[index].close(channel);
116+
}
117+
} catch (Throwable e) {
118+
if (first == null) {
119+
first = e;
120+
} else {
121+
first.addSuppressed(e);
122+
}
123+
}
124+
}
125+
if (first != null) {
126+
if (first instanceof IOException ioe) {
127+
throw ioe;
128+
} else if (first instanceof InterruptedException ie) {
129+
throw ie;
130+
} else if (first instanceof RuntimeException re) {
131+
throw re;
132+
} else {
133+
throw new IOException("Error closing credentials.", first);
134+
}
135+
}
136+
}
137+
}
138+
139+
@SuppressFBWarnings(value = {"NP_UNWRITTEN_FIELD", "UWF_NULL_FIELD"})
140+
@Deprecated
141+
private static final class CompositeKeyMaterial extends KeyMaterial implements Serializable {
142+
143+
private static final long serialVersionUID = 1L;
144+
145+
private final KeyMaterial[] keyMaterials = null;
146+
147+
private CompositeKeyMaterial() {
148+
super(null);
149+
assert false : "only deserialized";
150+
}
151+
109152
@Override
110153
public void close() throws IOException {
111154
Throwable first = null;

src/main/java/org/jenkinsci/plugins/docker/commons/impl/NullKeyMaterialFactory.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,26 @@
2424
package org.jenkinsci.plugins.docker.commons.impl;
2525

2626
import edu.umd.cs.findbugs.annotations.NonNull;
27-
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial;
2827
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialContext;
2928
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialFactory;
3029
import org.kohsuke.accmod.Restricted;
3130
import org.kohsuke.accmod.restrictions.NoExternalUse;
3231

3332
import java.io.IOException;
33+
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial2;
3434

3535
/**
3636
* {@link org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialFactory} that does nothing.
3737
*
38-
* @see org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial#NULL
38+
* @see org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial2#NULL
3939
* @author Kohsuke Kawaguchi
4040
*/
4141
@Restricted(NoExternalUse.class)
4242
public final class NullKeyMaterialFactory extends KeyMaterialFactory {
4343

4444
@Override
45-
public KeyMaterial materialize() throws IOException, InterruptedException {
46-
return KeyMaterial.NULL;
45+
public KeyMaterial2 materialize2() throws IOException, InterruptedException {
46+
return KeyMaterial2.NULL;
4747
}
4848

4949
@Override

0 commit comments

Comments
 (0)