3

I have this code:

import * as Docker from 'dockerode'
const docker = new Docker()
const remoteDockerImage = docker.getImage(`${awsRepoUrl}:${version}`)

await remoteDockerImage.push({
  authconfig: { base64: 'auth token from aws' },
  tag: version,
})

After I run this there is no error, even if I put a on("error") on the return of .push.

And this creates the following in docker logs

[18:47:06.056][ApiProxy       ][Info   ] time="2019-01-18T18:47:06+08:00" msg="proxy >> POST /images/284135xxxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxx:v0.1.200/push?tag=v0.1.200\n"
[18:49:18.018][DnsUpdater     ][Error  ] Unable to update dns settings: Value cannot be null.
Parameter name: first
[18:49:18.018][DnsUpdater     ][Info   ] Network configuration change detected
[18:49:19.068][DnsUpdater     ][Error  ] Unable to update dns settings: Value cannot be null.

And nothing happens, no image is pushed. not sure what are those dns errors...

But now when I go to the CMD line and I run docker login -u AWS -p secretAWSkey https://284135xxxxxx.dkr.ecr.us-east-1.amazonaws.com and then docker push my-image I see in the logs:

[18:57:17.517][ApiProxy       ][Info   ] time="2019-01-18T18:57:17+08:00" msg="proxy << POST /v1.39/auth (5.1241041s)\n"
[18:57:17.694][ApiProxy       ][Info   ] time="2019-01-18T18:57:17+08:00" msg="proxy >> GET /_ping\n"
[18:57:17.699][ApiProxy       ][Info   ] time="2019-01-18T18:57:17+08:00" msg="proxy << GET /_ping (3.9935ms)\n"
[18:57:18.107][ApiProxy       ][Info   ] time="2019-01-18T18:57:18+08:00" msg="proxy >> POST /v1.39/images/284135xxxxxx.dkr.ecr.us-east-1.amazonaws.com/app-repo/push?tag=v0.1.206\n"

Now the image is pushed and working.

Differences (working vs not working):

  • /v1.39/images/ vs /images/
  • call to /v1.39/auth vs no call

Not sure what to do, as the api for dockerode is really bad or missing and I can't figure out how to do it. Any help is appreciated. thank you

Totty.js
  • 15,563
  • 31
  • 103
  • 175

3 Answers3

1

I was able to push to ECR using dockerode with the following code:

The image name needs to be your repo URL

const repo = '1111111111.dkr.ecr.us-east-1.amazonaws.com/my-repository-name'
const name = repo
const tag = generateGuid()

When building the image, set the name and tag as below:

        const buildStream = await docker.buildImage(path, { t: `${name}:${tag}` })

Listen to the buildstream so that you know when it's complete.

buildStream.pipe(process.stdout)
const imageStream: any = await new Promise((resolve, reject) => {
   docker.modem.followProgress(
      buildStream,
      (err: Error | null, res: any) => (err ? reject(err) : resolve(res))
   )
})

You can extract the image hash from the buildStream if needed

const hash = imageStream.find((p: any) => Object.keys(p)[0] === 'aux').aux.ID

Get the authconfig using ECR Client

const ecrClient = new ECRClient({ region: 'us-east-1' })
const data = await ecrClient.send(new GetAuthorizationTokenCommand({}))
if (!data?.authorizationData) throw new Error('No authorizationData')
    
let authInfo = data.authorizationData[0]
let [user, pass] = Buffer.from(authInfo.authorizationToken ?? '', 'base64')
   .toString()
   .split(':')
const authconfig  = {
   username: user,
   password: pass,
   serveraddress: authInfo?.proxyEndpoint || '',
}
console.log('authconfig : ', authconfig)

const image = docker.getImage(name)

tag and push your image. You'll want to listen to the pushStream so that you know when the push is complete. Otherwise the process will exit early

console.log('tagging')
await image.tag({
   repo,
   tag,
})

console.log('pushing')
const pushStream = await image.push({
   authconfig,
   tag,
})
pushStream.pipe(process.stdout)
await new Promise((resolve, reject) => {
   docker.modem.followProgress(pushStream, (err: Error | null, res: any) =>
      err ? reject(err) : resolve(res)
   )
})
Zach Fey
  • 95
  • 7
  • I moved away from dockerode because I was never able to push the image, it became so much easier to spawn and run the docker commands, but thank you for taking the time to put an answer for this, I will give it another try using your answer :) – Purefan Aug 30 '23 at 08:01
0

As per dockerode's README.md file, you might need to swap "base64" for "key" in the authconfig object.

Please look a the pull example here : https://github.com/apocas/dockerode/blob/master/README.md#pull-from-private-repos

Clovel
  • 58
  • 10
0

I know this is an old question, but I found the answer for myself to be that the stream needs to be listened to.

const isErrorMessage = (message) => 'error' in message && !!message.error;
const followProgress = async (
  stream,
  docker,
  onProgress,
) => {
  await new Promise((resolve, reject) => {
    docker.modem.followProgress(
      stream,
      (err, result) => err ? reject(err) : resolve(err),
      (progress) => {
        if (isErrorMessage(progress)) {
          reject(new Error(progress.error));
        } else {
          onProgress(progress);
        }
      },
    );
  });
}

const stream = await repoImage.push({ authconfig, tag });
const onProgress = (progress) => logger.debug(`${progress.id ? `(${progress.id})` : ''}: ${progress.status}${progress.progress ? ` ${progress.progress}` : ''}`);
await followProgress(stream, docker, onProgress);

david.tanner
  • 529
  • 3
  • 8
  • 13