1
1
import * as fs from 'fs' ;
2
2
import sleep from 'sleep-promise' ;
3
+ import crypto from 'crypto' ;
3
4
4
5
import { logger } from '../../common/logger' ;
5
6
import { config } from '../../common/config' ;
6
7
import * as processWrapper from '../../common/process' ;
7
8
import * as credentials from './credentials' ;
8
- import { SkopeoRepositoryType } from './types' ;
9
+ import { ImageDigests , ImageManifest , SkopeoRepositoryType } from './types' ;
10
+
11
+ const DEFAULT_PLATFORM_OS = 'linux' ;
12
+ const DEFAULT_PLATFORM_ARCH = 'amd64' ;
9
13
10
14
function getUniqueIdentifier ( ) : string {
11
15
const [ seconds , nanoseconds ] = process . hrtime ( ) ;
@@ -37,10 +41,10 @@ export async function pull(
37
41
destination : string ,
38
42
skopeoRepoType : SkopeoRepositoryType ,
39
43
workloadName : string ,
40
- ) : Promise < void > {
44
+ ) : Promise < ImageDigests > {
41
45
const creds = await credentials . getSourceCredentials ( image ) ;
42
- const credentialsParameters = getCredentialParameters ( creds ) ;
43
- const certificatesParameters = getCertificatesParameters ( ) ;
46
+ const credentialsParameters = getCopyCredentialParameters ( creds ) ;
47
+ const certificatesParameters = getCopyCertificatesParameters ( ) ;
44
48
45
49
const args : Array < processWrapper . IProcessArgument > = [ ] ;
46
50
args . push ( { body : 'copy' , sanitise : false } ) ;
@@ -57,11 +61,26 @@ export async function pull(
57
61
sanitise : false ,
58
62
} ) ;
59
63
60
- await pullWithRetry ( args , destination , workloadName ) ;
64
+ const env : Record < string , string | undefined > = {
65
+ // The Azure CR credentials helper requires these env vars:
66
+ AZURE_CLIENT_ID : process . env . AZURE_CLIENT_ID ,
67
+ AZURE_TENANT_ID : process . env . AZURE_TENANT_ID ,
68
+ AZURE_FEDERATED_TOKEN_FILE : process . env . AZURE_FEDERATED_TOKEN_FILE ,
69
+ AZURE_FEDERATED_TOKEN : process . env . AZURE_FEDERATED_TOKEN ,
70
+ AZURE_AUTHORITY_HOST : process . env . AZURE_AUTHORITY_HOST ,
71
+ } ;
72
+ await pullWithRetry ( args , env , destination , workloadName ) ;
73
+
74
+ return await extractImageDigests (
75
+ prefixRespository ( image , SkopeoRepositoryType . ImageRegistry ) ,
76
+ env ,
77
+ creds ,
78
+ ) ;
61
79
}
62
80
63
81
async function pullWithRetry (
64
82
args : Array < processWrapper . IProcessArgument > ,
83
+ env : Record < string , string | undefined > ,
65
84
destination : string ,
66
85
workloadName : string ,
67
86
) : Promise < void > {
@@ -70,17 +89,9 @@ async function pullWithRetry(
70
89
71
90
for ( let attempt = 1 ; attempt <= MAX_ATTEMPTS ; attempt ++ ) {
72
91
try {
73
- const env : Record < string , string | undefined > = {
74
- // The Azure CR credentials helper requires these env vars:
75
- AZURE_CLIENT_ID : process . env . AZURE_CLIENT_ID ,
76
- AZURE_TENANT_ID : process . env . AZURE_TENANT_ID ,
77
- AZURE_FEDERATED_TOKEN_FILE : process . env . AZURE_FEDERATED_TOKEN_FILE ,
78
- AZURE_FEDERATED_TOKEN : process . env . AZURE_FEDERATED_TOKEN ,
79
- AZURE_AUTHORITY_HOST : process . env . AZURE_AUTHORITY_HOST ,
80
- } ;
81
92
await processWrapper . exec ( 'skopeo' , env , ...args ) ;
82
93
return ;
83
- } catch ( err ) {
94
+ } catch ( err : unknown ) {
84
95
try {
85
96
if ( fs . existsSync ( destination ) ) {
86
97
fs . unlinkSync ( destination ) ;
@@ -99,7 +110,60 @@ async function pullWithRetry(
99
110
}
100
111
}
101
112
102
- export function getCredentialParameters (
113
+ export async function extractImageDigests (
114
+ image : string ,
115
+ env : Record < string , string | undefined > = { } ,
116
+ creds ?: string ,
117
+ ) : Promise < ImageDigests > {
118
+ let indexDigest : string | undefined = undefined ;
119
+ let manifestDigest : string | undefined = undefined ;
120
+
121
+ const args : Array < processWrapper . IProcessArgument > = [
122
+ { body : 'inspect' , sanitise : false } ,
123
+ { body : '--raw' , sanitise : false } ,
124
+ { body : image , sanitise : false } ,
125
+ ...getInspectCredentialParameters ( creds ) ,
126
+ ...getInspectCertificatesParameters ( ) ,
127
+ ] ;
128
+
129
+ try {
130
+ const { stdout } = await processWrapper . exec ( 'skopeo' , env , ...args ) ;
131
+ const manifest = JSON . parse ( stdout ) as ImageManifest ;
132
+ if ( isIndex ( manifest ) ) {
133
+ manifestDigest = manifest . manifests ?. find (
134
+ ( m ) =>
135
+ m . platform . os === DEFAULT_PLATFORM_OS &&
136
+ m . platform . architecture === DEFAULT_PLATFORM_ARCH ,
137
+ ) ?. digest ;
138
+ indexDigest = manifestDigest ? calculateDigest ( stdout ) : undefined ;
139
+ } else {
140
+ manifestDigest = calculateDigest ( stdout ) ;
141
+ }
142
+ } catch ( error ) {
143
+ logger . warn (
144
+ { error } ,
145
+ 'could not get the image digests through Skopeo inspect-raw' ,
146
+ ) ;
147
+ }
148
+ return { indexDigest, manifestDigest } ;
149
+ }
150
+
151
+ function isIndex ( manifest : ImageManifest ) : boolean {
152
+ return (
153
+ manifest . mediaType . includes ( 'vnd.oci.image.index' ) ||
154
+ manifest . mediaType . includes ( 'vnd.docker.distribution.manifest.list' )
155
+ ) ;
156
+ }
157
+
158
+ function calculateDigest ( manifest : string ) : string {
159
+ return `sha256:${ crypto
160
+ . createHash ( 'sha256' )
161
+ . update ( manifest )
162
+ . digest ( 'hex' )
163
+ . toString ( ) } `;
164
+ }
165
+
166
+ export function getCopyCredentialParameters (
103
167
credentials : string | undefined ,
104
168
) : Array < processWrapper . IProcessArgument > {
105
169
const credentialsParameters : Array < processWrapper . IProcessArgument > = [ ] ;
@@ -110,9 +174,27 @@ export function getCredentialParameters(
110
174
return credentialsParameters ;
111
175
}
112
176
113
- export function getCertificatesParameters ( ) : Array < processWrapper . IProcessArgument > {
177
+ export function getInspectCredentialParameters (
178
+ credentials ?: string ,
179
+ ) : Array < processWrapper . IProcessArgument > {
180
+ const credentialsParameters : Array < processWrapper . IProcessArgument > = [ ] ;
181
+ if ( credentials ) {
182
+ credentialsParameters . push ( { body : '--creds' , sanitise : true } ) ;
183
+ credentialsParameters . push ( { body : credentials , sanitise : true } ) ;
184
+ }
185
+ return credentialsParameters ;
186
+ }
187
+
188
+ export function getCopyCertificatesParameters ( ) : Array < processWrapper . IProcessArgument > {
114
189
const certificatesParameters : Array < processWrapper . IProcessArgument > = [ ] ;
115
190
certificatesParameters . push ( { body : '--src-cert-dir' , sanitise : true } ) ;
116
191
certificatesParameters . push ( { body : '/srv/app/certs' , sanitise : true } ) ;
117
192
return certificatesParameters ;
118
193
}
194
+
195
+ export function getInspectCertificatesParameters ( ) : Array < processWrapper . IProcessArgument > {
196
+ const certificatesParameters : Array < processWrapper . IProcessArgument > = [ ] ;
197
+ certificatesParameters . push ( { body : '--cert-dir' , sanitise : true } ) ;
198
+ certificatesParameters . push ( { body : '/srv/app/certs' , sanitise : true } ) ;
199
+ return certificatesParameters ;
200
+ }
0 commit comments