17
17
18
18
import java .io .BufferedInputStream ;
19
19
import java .io .InputStream ;
20
+ import java .net .URI ;
21
+ import java .net .URISyntaxException ;
20
22
import java .time .Duration ;
23
+ import java .time .Instant ;
21
24
import java .util .Map ;
25
+ import java .util .Optional ;
22
26
import java .util .function .Predicate ;
23
27
24
28
import org .openjdk .jmc .flightrecorder .rules .IRule ;
27
31
import io .cryostat .core .reports .InterruptibleReportGenerator ;
28
32
import io .cryostat .core .reports .InterruptibleReportGenerator .AnalysisResult ;
29
33
import io .cryostat .recordings .ActiveRecording ;
34
+ import io .cryostat .recordings .ArchivedRecordings .ArchivedRecording ;
30
35
import io .cryostat .recordings .RecordingHelper ;
31
36
32
37
import com .fasterxml .jackson .databind .ObjectMapper ;
33
38
import io .smallrye .mutiny .Uni ;
34
39
import jakarta .enterprise .context .ApplicationScoped ;
35
40
import jakarta .inject .Inject ;
41
+ import jakarta .ws .rs .InternalServerErrorException ;
42
+ import org .apache .commons .lang3 .tuple .Pair ;
36
43
import org .eclipse .microprofile .config .inject .ConfigProperty ;
37
44
import org .eclipse .microprofile .rest .client .inject .RestClient ;
38
45
import org .jboss .logging .Logger ;
46
+ import software .amazon .awssdk .services .s3 .model .GetObjectRequest ;
47
+ import software .amazon .awssdk .services .s3 .presigner .S3Presigner ;
48
+ import software .amazon .awssdk .services .s3 .presigner .model .GetObjectPresignRequest ;
39
49
40
50
@ ApplicationScoped
41
51
class ReportsServiceImpl implements ReportsService {
@@ -48,10 +58,20 @@ class ReportsServiceImpl implements ReportsService {
48
58
@ ConfigProperty (name = ConfigProperties .REPORTS_SIDECAR_URL )
49
59
String sidecarUri ;
50
60
61
+ @ ConfigProperty (name = ConfigProperties .REPORTS_USE_PRESIGNED_TRANSFER )
62
+ boolean usePresignedTransfer ;
63
+
64
+ @ ConfigProperty (name = ConfigProperties .AWS_BUCKET_NAME_ARCHIVES )
65
+ String archiveBucket ;
66
+
67
+ @ ConfigProperty (name = ConfigProperties .STORAGE_EXT_URL )
68
+ Optional <String > externalStorageUrl ;
69
+
51
70
@ Inject ObjectMapper mapper ;
52
71
@ Inject RecordingHelper helper ;
53
72
@ Inject InterruptibleReportGenerator reportGenerator ;
54
73
@ Inject @ RestClient ReportSidecarService sidecar ;
74
+ @ Inject S3Presigner presigner ;
55
75
@ Inject Logger logger ;
56
76
57
77
@ Override
@@ -63,7 +83,7 @@ public Uni<Map<String, AnalysisResult>> reportFor(
63
83
} catch (Exception e ) {
64
84
throw new ReportGenerationException (e );
65
85
}
66
- if (NO_SIDECAR_URL . equals ( sidecarUri )) {
86
+ if (! useSidecar ( )) {
67
87
logger .tracev (
68
88
"inprocess reportFor active recording {0} {1}" ,
69
89
recording .target .jvmId , recording .remoteId );
@@ -72,7 +92,7 @@ public Uni<Map<String, AnalysisResult>> reportFor(
72
92
logger .tracev (
73
93
"sidecar reportFor active recording {0} {1}" ,
74
94
recording .target .jvmId , recording .remoteId );
75
- return fireRequest (stream );
95
+ return sidecar . generate (stream );
76
96
}
77
97
}
78
98
@@ -85,12 +105,22 @@ public Uni<Map<String, AnalysisResult>> reportFor(
85
105
} catch (Exception e ) {
86
106
throw new ReportGenerationException (e );
87
107
}
88
- if (NO_SIDECAR_URL . equals ( sidecarUri )) {
108
+ if (! useSidecar ( )) {
89
109
logger .tracev ("inprocess reportFor archived recording {0} {1}" , jvmId , filename );
90
110
return process (stream , predicate );
111
+ } else if (usePresignedSidecar ()) {
112
+ logger .tracev (
113
+ "sidecar reportFor presigned archived recording {0} {1}" , jvmId , filename );
114
+ try {
115
+ var uri = getPresignedPath (jvmId , filename );
116
+ return sidecar .generatePresigned (uri .getPath (), uri .getQuery (), null );
117
+ } catch (URISyntaxException e ) {
118
+ logger .error (e );
119
+ throw new InternalServerErrorException (e );
120
+ }
91
121
} else {
92
122
logger .tracev ("sidecar reportFor archived recording {0} {1}" , jvmId , filename );
93
- return fireRequest (stream );
123
+ return sidecar . generate (stream );
94
124
}
95
125
}
96
126
@@ -104,6 +134,24 @@ public Uni<Map<String, AnalysisResult>> reportFor(String jvmId, String filename)
104
134
return reportFor (jvmId , filename , r -> true );
105
135
}
106
136
137
+ @ Override
138
+ public boolean keyExists (ActiveRecording recording ) {
139
+ return false ;
140
+ }
141
+
142
+ @ Override
143
+ public boolean keyExists (String jvmId , String filename ) {
144
+ return false ;
145
+ }
146
+
147
+ private boolean useSidecar () {
148
+ return sidecarUri != null && !sidecarUri .isBlank () && !NO_SIDECAR_URL .equals (sidecarUri );
149
+ }
150
+
151
+ private boolean usePresignedSidecar () {
152
+ return useSidecar () && usePresignedTransfer ;
153
+ }
154
+
107
155
private Uni <Map <String , AnalysisResult >> process (
108
156
InputStream stream , Predicate <IRule > predicate ) {
109
157
return Uni .createFrom ()
@@ -112,23 +160,33 @@ private Uni<Map<String, AnalysisResult>> process(
112
160
new BufferedInputStream (stream ), predicate ));
113
161
}
114
162
115
- private Uni <Map <String , AnalysisResult >> fireRequest (InputStream stream ) {
116
- return sidecar .generate (stream );
163
+ private URI getPresignedUri (ActiveRecording recording ) throws Exception {
164
+ // TODO refactor, this is copied out of Recordings.java
165
+ String savename = recording .name ;
166
+ ArchivedRecording rec =
167
+ helper .archiveRecording (recording , savename , Instant .now ().plusSeconds (60 ));
168
+ return getPresignedPath (recording .target .jvmId , rec .name ());
169
+ }
170
+
171
+ private URI getPresignedPath (String jvmId , String filename ) throws URISyntaxException {
172
+ // TODO refactor, this is copied out of Recordings.java
173
+ logger .infov ("Handling presigned download request for {0}/{1}" , jvmId , filename );
174
+ GetObjectRequest getRequest =
175
+ GetObjectRequest .builder ()
176
+ .bucket (archiveBucket )
177
+ .key (helper .archivedRecordingKey (Pair .of (jvmId , filename )))
178
+ .build ();
179
+ GetObjectPresignRequest presignRequest =
180
+ GetObjectPresignRequest .builder ()
181
+ .signatureDuration (Duration .ofMinutes (1 ))
182
+ .getObjectRequest (getRequest )
183
+ .build ();
184
+ return URI .create (presigner .presignGetObject (presignRequest ).url ().toString ()).normalize ();
117
185
}
118
186
119
187
public static class ReportGenerationException extends RuntimeException {
120
188
public ReportGenerationException (Throwable cause ) {
121
189
super (cause );
122
190
}
123
191
}
124
-
125
- @ Override
126
- public boolean keyExists (ActiveRecording recording ) {
127
- return false ;
128
- }
129
-
130
- @ Override
131
- public boolean keyExists (String jvmId , String filename ) {
132
- return false ;
133
- }
134
192
}
0 commit comments