6
6
import static com .redhat .hacbs .container .analyser .build .BuildInfo .MAVEN ;
7
7
import static com .redhat .hacbs .container .analyser .build .BuildInfo .SBT ;
8
8
import static com .redhat .hacbs .container .analyser .build .gradle .GradleUtils .GOOGLE_JAVA_FORMAT_PLUGIN ;
9
+ import static org .apache .commons .lang3 .StringUtils .isBlank ;
10
+ import static org .apache .commons .lang3 .StringUtils .isNotBlank ;
9
11
import static org .eclipse .jgit .api .ResetCommand .ResetType .HARD ;
10
12
11
13
import java .io .BufferedReader ;
14
+ import java .io .ByteArrayOutputStream ;
12
15
import java .io .IOException ;
13
16
import java .net .URI ;
17
+ import java .nio .charset .Charset ;
14
18
import java .nio .file .Files ;
15
19
import java .nio .file .Path ;
16
20
import java .util .ArrayList ;
21
+ import java .util .Arrays ;
17
22
import java .util .Collection ;
18
23
import java .util .Collections ;
19
24
import java .util .HashSet ;
20
25
import java .util .List ;
21
26
import java .util .Map ;
22
27
import java .util .Objects ;
28
+ import java .util .Optional ;
23
29
import java .util .Set ;
24
30
25
31
import jakarta .enterprise .inject .Instance ;
26
32
import jakarta .inject .Inject ;
27
33
28
34
import org .apache .commons .cli .ParseException ;
35
+ import org .apache .commons .codec .digest .DigestUtils ;
29
36
import org .apache .maven .cli .CLIManager ;
30
37
import org .apache .maven .model .Model ;
31
38
import org .apache .maven .model .io .xpp3 .MavenXpp3Reader ;
32
39
import org .eclipse .jgit .api .Git ;
40
+ import org .eclipse .microprofile .config .inject .ConfigProperty ;
33
41
import org .eclipse .microprofile .rest .client .RestClientBuilder ;
34
42
35
43
import com .fasterxml .jackson .databind .ObjectMapper ;
44
+ import com .google .cloud .tools .jib .api .Credential ;
45
+ import com .google .cloud .tools .jib .api .ImageReference ;
46
+ import com .google .cloud .tools .jib .api .InvalidImageReferenceException ;
47
+ import com .google .cloud .tools .jib .api .RegistryException ;
48
+ import com .google .cloud .tools .jib .blob .Blob ;
49
+ import com .google .cloud .tools .jib .image .json .ManifestTemplate ;
50
+ import com .google .cloud .tools .jib .image .json .OciManifestTemplate ;
51
+ import com .google .cloud .tools .jib .registry .ManifestAndDigest ;
52
+ import com .google .cloud .tools .jib .registry .RegistryClient ;
36
53
import com .redhat .hacbs .container .analyser .build .ant .AntUtils ;
37
54
import com .redhat .hacbs .container .analyser .build .gradle .GradleUtils ;
38
55
import com .redhat .hacbs .container .analyser .build .maven .MavenDiscoveryTask ;
56
+ import com .redhat .hacbs .container .analyser .deploy .containerregistry .ContainerUtil ;
39
57
import com .redhat .hacbs .container .analyser .location .VersionRange ;
40
58
import com .redhat .hacbs .recipies .build .BuildRecipeInfo ;
41
59
import com .redhat .hacbs .recipies .scm .ScmInfo ;
42
60
import com .redhat .hacbs .recipies .util .GitCredentials ;
61
+ import com .redhat .hacbs .resources .model .v1alpha1 .Util ;
62
+ import com .redhat .hacbs .resources .model .v1alpha1 .jbsconfigstatus .ImageRegistry ;
43
63
64
+ import io .fabric8 .kubernetes .client .KubernetesClientException ;
44
65
import io .quarkus .logging .Log ;
66
+ import io .vertx .core .json .JsonObject ;
45
67
import picocli .CommandLine ;
46
68
47
69
@ CommandLine .Command (name = "lookup-build-info" )
@@ -53,11 +75,14 @@ public class LookupBuildInfoCommand implements Runnable {
53
75
@ CommandLine .Option (names = "--scm-url" , required = true )
54
76
String scmUrl ;
55
77
78
+ @ CommandLine .Option (names = "--scm-commit" , required = true )
79
+ String commit ;
80
+
56
81
@ CommandLine .Option (names = "--scm-tag" , required = true )
57
82
String tag ;
58
83
59
- // This is a subdirectory to operate upon.
60
- @ CommandLine .Option (names = "--context" , required = true )
84
+ // This is a subdirectory to operate upon (i.e. path)
85
+ @ CommandLine .Option (names = "--context" )
61
86
String context ;
62
87
63
88
@ CommandLine .Option (names = "--version" , required = true )
@@ -68,6 +93,13 @@ public class LookupBuildInfoCommand implements Runnable {
68
93
69
94
@ CommandLine .Option (names = "--private-repo" )
70
95
boolean privateRepo ;
96
+
97
+ @ CommandLine .Option (names = "--registries" , description = "Denotes registries to search for preexisting builds" )
98
+ String registries ;
99
+
100
+ @ ConfigProperty (name = "registry.token" )
101
+ Optional <String > envToken ;
102
+
71
103
/**
72
104
* The build info, in JSON format as per BuildRecipe.
73
105
* <p>
@@ -84,7 +116,7 @@ public class LookupBuildInfoCommand implements Runnable {
84
116
public void run () {
85
117
try {
86
118
ScmInfo info = new ScmInfo ("git" , this .scmUrl );
87
- Log .infof ("LookupBuildInfo:: resolving " + info .getUri ());
119
+ Log .infof ("LookupBuildInfo resolving %s for version %s " , info .getUri (), version );
88
120
89
121
CacheBuildInfoLocator buildInfoLocator = RestClientBuilder .newBuilder ().baseUri (new URI (cacheUrl ))
90
122
.build (CacheBuildInfoLocator .class );
@@ -99,8 +131,8 @@ public void run() {
99
131
}
100
132
}
101
133
102
- Log .infof ("Checking out %s at tag %s" , scmUrl , tag );
103
- doBuildAnalysis (info .getUriWithoutFragment (), tag , context , buildRecipeInfo , privateRepo , buildInfoLocator );
134
+ Log .infof ("Cloning commit %s ( tag %s) for path %s " , commit , tag , context );
135
+ doBuildAnalysis (info .getUriWithoutFragment (), commit , context , buildRecipeInfo , privateRepo , buildInfoLocator );
104
136
105
137
if (message != null ) {
106
138
Files .createFile (message );
@@ -133,7 +165,7 @@ private void doBuildAnalysis(String scmUrl, String scmTag, String context, Build
133
165
skipTests = false ;
134
166
}
135
167
long time = clone .getRepository ().parseCommit (clone .getRepository ().resolve (scmTag )).getCommitTime () * 1000L ;
136
- if (context != null ) {
168
+ if (isNotBlank ( context ) ) {
137
169
path = path .resolve (context );
138
170
}
139
171
BuildInfo info = new BuildInfo ();
@@ -165,8 +197,7 @@ private void doBuildAnalysis(String scmUrl, String scmTag, String context, Build
165
197
try (BufferedReader pomReader = Files .newBufferedReader (pomFile )) {
166
198
MavenXpp3Reader reader = new MavenXpp3Reader ();
167
199
Model model = reader .read (pomReader );
168
-
169
- //TODO: we should do discoery on the whole tree
200
+ //TODO: we should do discovery on the whole tree
170
201
List <DiscoveryResult > results = new ArrayList <>();
171
202
if (model .getVersion () != null && model .getVersion ().endsWith ("-SNAPSHOT" )) {
172
203
//not tagged properly, deal with it automatically
@@ -310,6 +341,79 @@ private void doBuildAnalysis(String scmUrl, String scmTag, String context, Build
310
341
info .setAllowedDifferences (buildRecipeInfo .getAllowedDifferences ());
311
342
Log .infof ("Got build recipe info %s" , buildRecipeInfo );
312
343
}
344
+ if (registries != null ) {
345
+ String [] splitRegistries = registries .split (";" , -1 );
346
+
347
+ for (String value : splitRegistries ) {
348
+ ImageRegistry registry = Util .parseRegistry (value );
349
+ // Meant to match Go code that does
350
+ // util.HashString(abr.Status.SCMInfo.SCMURL + abr.Status.SCMInfo.Tag + abr.Status.SCMInfo.Path)
351
+ String contextPath = context == null ? "" : context ;
352
+ String prependTag = isBlank (registry .getPrependTag ()) ? "" : registry .getPrependTag () + "_" ;
353
+ String imageId = prependTag + DigestUtils .md5Hex (scmUrl + tag + contextPath );
354
+ String port = isBlank (registry .getPort ()) ? "443" : registry .getPort ();
355
+ String fullName = registry .getHost () + (port .equals ("443" ) ? "" : ":" + port ) + "/" + registry .getOwner ()
356
+ + "/" + registry .getRepository () + ":" + imageId ;
357
+ Credential credential = ContainerUtil .processToken (fullName , envToken .orElse ("" ));
358
+
359
+ Log .infof ("Examining registry %s for image %s" , fullName , imageId );
360
+ try {
361
+ //TODO consider authentication whether via env token or dockerconfig. Would need to pass
362
+ // token in env as per ContainerRegistryDeployer?
363
+ ImageReference reference = ImageReference .parse (fullName );
364
+ RegistryClient registryClient = ContainerUtil .getRegistryClient (reference , credential ,
365
+ registry .getInsecure ());
366
+ Optional <ManifestAndDigest <ManifestTemplate >> manifestAndDigest = registryClient
367
+ .checkManifest (reference .getTag ().get ());
368
+
369
+ if (manifestAndDigest .isPresent ()) {
370
+ // Found a potential manifest match for the build - now obtain the container manifest JSON
371
+ // so we can obtain the correct GAVs.
372
+ Blob blob = registryClient .pullBlob (((OciManifestTemplate ) manifestAndDigest .get ()
373
+ .getManifest ()).getContainerConfiguration ()
374
+ .getDigest (),
375
+ s -> {
376
+ }, s -> {
377
+ });
378
+
379
+ ByteArrayOutputStream bao = new ByteArrayOutputStream ();
380
+ blob .writeTo (bao );
381
+ String fromManifest = bao .toString (Charset .defaultCharset ());
382
+ JsonObject tagListJson = new JsonObject (fromManifest );
383
+ JsonObject configJson = tagListJson .getJsonObject ("config" );
384
+ JsonObject labels = configJson .getJsonObject ("Labels" );
385
+ // ContainerRegistryDeployer uses a comma separated list
386
+ List <String > gavList ;
387
+ if (labels .getMap ().containsKey ("io.jvmbuildservice.gavs" )) {
388
+ gavList = Arrays .asList (
389
+ labels .getString ("io.jvmbuildservice.gavs" ).split ("," , -1 ));
390
+ } else {
391
+ // Backwards compatibility check for builds with older label format.
392
+ String g = labels .getString ("groupId" );
393
+ String v = labels .getString ("version" );
394
+ gavList = new ArrayList <>();
395
+ Arrays .stream (labels .getString ("artifactId" ).split ("," ))
396
+ .forEach (a -> gavList .add (g + ":" + a + ":" + v ));
397
+ }
398
+ info .setGavs (gavList );
399
+ info .setImage (fullName );
400
+ info .setDigest (manifestAndDigest .get ().getDigest ().toString ());
401
+ Log .infof (
402
+ "Found GAVs " + gavList + " and digest " + manifestAndDigest .get ()
403
+ .getDigest ());
404
+ break ;
405
+ }
406
+ } catch (InvalidImageReferenceException | IOException | RegistryException e ) {
407
+ // Manually deleting a tag on quay.io gives
408
+ // Tried to pull image manifest for <...>:975ea3800099190263d38f051c1a188a but failed
409
+ // because: unknown error code: TAG_EXPIRED
410
+ if (!e .getMessage ().contains ("TAG_EXPIRED" )) {
411
+ throw new KubernetesClientException (e .toString ());
412
+ }
413
+ Log .errorf ("Registry tag expired - " + e .getMessage ());
414
+ }
415
+ }
416
+ }
313
417
ObjectMapper mapper = new ObjectMapper ();
314
418
Log .infof ("Writing %s to %s" , info , buildInfo .toFile ());
315
419
mapper .writeValue (buildInfo .toFile (), info );
0 commit comments