/**********************************************************************
 * Copyright (c) 2021 Red Hat, Inc.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 ***********************************************************************/ import * as crypto from 'crypto' ; import { injectable , postConstruct } from 'inversify' ; import simpleGit , { SimpleGit } from 'simple-git' ; import Axios from 'axios' ; import { parse } from 'docker-image-name-parser' ; /**
 * Allow to grab information from docker.io and quay.io
 */ @ injectable ( ) export class RegistryHelper { private shortSha1 : string ; private git : SimpleGit ; constructor ( ) { this . git = simpleGit ( { maxConcurrentProcesses : 1 } ) ; } @ postConstruct ( ) async init ( ) { const gitRootDirectory = await this . git . revparse ( [ '--show-toplevel' ] ) ; const logOptions = { format : { hash : '%H' } , file : gitRootDirectory , n : '1' , } ; const result = await this . git . log ( logOptions ) ; const hash = ( result . latest as any ) . hash ; this . shortSha1 = hash . substring ( 0 , 7 ) ; } async getImageDigest ( imageName ) { if ( imageName . startsWith ( 'docker.io' ) ) { imageName = imageName . replace ( 'docker.io' , 'index.docker.io' ) ; } const dockerImageName = parse ( imageName ) ; if ( dockerImageName . tag === 'nightly' || dockerImageName . tag === 'next' || dockerImageName . tag === 'insiders' ) { return imageName ; } if ( dockerImageName . tag && dockerImageName . tag . includes ( this . shortSha1 ) ) { console . log ( `Do not fetch digest for ${ imageName } as the tag ${ dockerImageName . tag } includes the current sha1 ${ this . shortSha1 }` ) ; return imageName ; } const uri = `https://${ dockerImageName . host }/v2/${ dockerImageName . remoteName }/manifests/${ dockerImageName . tag }` ; let token ; if ( dockerImageName . host === 'index.docker.io' ) { const tokenUri = `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${ dockerImageName . remoteName }:pull` ; const tokenResponse = await Axios . get ( tokenUri , { headers : { Accept : 'application/json' , } , responseType : 'json' , } ) ; token = tokenResponse . data . token ; } const headers = { } as any ; if ( token ) { headers [ 'Authorization' ] = `Bearer ${ token }` ; } headers [ 'Accept' ] = 'application/vnd.docker.distribution.manifest.v2+json,application/vnd.docker.distribution.manifest.list.v2+json' ; const response = await Axios . get ( uri , { headers , responseType : 'arraybuffer' , } ) ; const content = Buffer . from ( response . data , 'binary' ) . toString ( ) ; const hash = crypto . createHash ( 'sha256' ) . update ( content ) . digest ( 'hex' ) ; const verifyUri = `https://${ dockerImageName . host }/v2/${ dockerImageName . remoteName }/manifests/sha256:${ hash }` ; await Axios . head ( verifyUri , { headers , responseType : 'arraybuffer' , } ) ; return `${ dockerImageName . host }/${ dockerImageName . remoteName }@sha256:${ hash }` ; } }