Downloading a charm from the Juju Controller

As part of investigating a bug, I wanted to do some independent verification of the charm content by doing a direct download via curl. Juju doesn’t make that particularly easy, because it does a fair bit of validation, and getting the URL formatted correctly was a bit of work. I had to instrument my agents and controller to work out what the right things were, and I ended up getting to this:

# You can get this value from the log line about "downloading"
charmurl="ch:amd64/jammy/github-runner-140"
filename="/tmp/${charmurl##*/}.zip"

cd /var/lib/juju/agents/unit*

# all these values are read from agent.conf
user=`awk '$1 == "tag:" {print($2)}' agent.conf`
password=`awk '$1 == "apipassword:" {print($2)}' agent.conf`
address=`grep -A1 apiaddresses agent.conf | awk '$1 == "-" {print($2)}'`
model=`awk '$1 == "model:" {print($2)}' agent.conf | sed 's/-/\//'`

curl -G --insecure \
  -o ${filename} \
  --user "${user}:${password}" \
  --data-urlencode "file=*" \
  --data-urlencode "url=${charmurl}" \
  "https://${address}/${model}/charms"

# and test that it downloaded correctly
unzip -t ${filename}

Juju uses basic auth, using tag names as the ‘username’ and the password that is stored in agent.conf. The API address is just an https address being served on a non-standard port (typically 17070). The above code is written assuming you only have 1 controller address.

The URL to GET a charm is found at:

https://$CONTROLLERADDRES:$CONTROLLERPORT/model/$MODELUUID/charms

With URL parameters of “file=*” and “url=$CHARMURL”. In the above script I use --data-urlencode to make sure those get escaped correctly (and -G to force it to do a GET instead of a POST request).

The final URL looks something like this:

https://10.139.162.57:17070/model/fca90071-fa5d-4fae-811f-5696cac9bfb2/charms?file=%2A&url=ch%3Aamd64%2Fjammy%2Fgithub-runner-140

I just realized we can do 1 better. We also have the CA certificate that we need to trust. So rather than doing --insecure we can do:

# extract the CA cert to a file, and trim the extra whitespace
awk '/^cacert:/{l=1;next;print;next} /^\S/{l=0} l' agent.conf | sed "s/^ *//" > /tmp/ca.cert

So all together it is:

# You can get this value from the log line about "downloading"
charmurl="ch:amd64/jammy/github-runner-140"
filename="/tmp/${charmurl##*/}.zip"

cd /var/lib/juju/agents/unit*

# all these values are read from agent.conf
user=`awk '$1 == "tag:" {print($2)}' agent.conf`
password=`awk '$1 == "apipassword:" {print($2)}' agent.conf`
address=`grep -A1 apiaddresses agent.conf | awk '$1 == "-" {print($2)}'`
model=`awk '$1 == "model:" {print($2)}' agent.conf | sed 's/-/\//'`

#extract the CA certificate
awk '/^cacert:/{l=1;next;print;next} /^\S/{l=0} l' agent.conf | sed "s/^ *//" > /tmp/ca.cert

curl -G --cacert /tmp/ca.cert \
  -o ${filename} \
  --user "${user}:${password}" \
  --data-urlencode "file=*" \
  --data-urlencode "url=${charmurl}" \
  "https://${address}/${model}/charms"

# and test that it downloaded correctly
unzip -t ${filename}
1 Like