Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prefetch-task-rhsm-integration #1205

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pipelines/docker-build-multi-platform-oci-ta/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/reposito
### prefetch-dependencies-oci-ta:0.1 task parameters
|name|description|default value|already set by|
|---|---|---|---|
|ACTIVATION_KEY| Name of secret which contains subscription activation key| activation-key| |
brianwcook marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit message:

s/THe/The

THe input is modified, injecting the entitlement certs

How is the input modified? Yes, we copy stuff to a shared volume, most importantly the certs that get passed to cachi2 to use for TLS auth, but other than that, how is the user input modified?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added example of how the input is modified.

|SOURCE_ARTIFACT| The Trusted Artifact URI pointing to the artifact with the application source code.| None| '$(tasks.clone-repository.results.SOURCE_ARTIFACT)'|
|caTrustConfigMapKey| The name of the key in the ConfigMap that contains the CA bundle data.| ca-bundle.crt| |
|caTrustConfigMapName| The name of the ConfigMap to read CA bundle data from.| trusted-ca| |
Expand Down
1 change: 1 addition & 0 deletions pipelines/docker-build-oci-ta/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/reposito
### prefetch-dependencies-oci-ta:0.1 task parameters
|name|description|default value|already set by|
|---|---|---|---|
|ACTIVATION_KEY| Name of secret which contains subscription activation key| activation-key| |
|SOURCE_ARTIFACT| The Trusted Artifact URI pointing to the artifact with the application source code.| None| '$(tasks.clone-repository.results.SOURCE_ARTIFACT)'|
|caTrustConfigMapKey| The name of the key in the ConfigMap that contains the CA bundle data.| ca-bundle.crt| |
|caTrustConfigMapName| The name of the ConfigMap to read CA bundle data from.| trusted-ca| |
Expand Down
1 change: 1 addition & 0 deletions pipelines/docker-build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/reposito
### prefetch-dependencies:0.1 task parameters
|name|description|default value|already set by|
|---|---|---|---|
|ACTIVATION_KEY| Name of secret which contains subscription activation key| activation-key| |
|caTrustConfigMapKey| The name of the key in the ConfigMap that contains the CA bundle data.| ca-bundle.crt| |
|caTrustConfigMapName| The name of the ConfigMap to read CA bundle data from.| trusted-ca| |
|config-file-content| Pass configuration to cachi2. Note this needs to be passed as a YAML-formatted config dump, not as a file path! | | |
Expand Down
1 change: 1 addition & 0 deletions pipelines/tekton-bundle-builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
### prefetch-dependencies:0.1 task parameters
|name|description|default value|already set by|
|---|---|---|---|
|ACTIVATION_KEY| Name of secret which contains subscription activation key| activation-key| |
|caTrustConfigMapKey| The name of the key in the ConfigMap that contains the CA bundle data.| ca-bundle.crt| |
|caTrustConfigMapName| The name of the ConfigMap to read CA bundle data from.| trusted-ca| |
|config-file-content| Pass configuration to cachi2. Note this needs to be passed as a YAML-formatted config dump, not as a file path! | | |
Expand Down
1 change: 1 addition & 0 deletions task/prefetch-dependencies-oci-ta/0.1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ params:
## Parameters
|name|description|default value|required|
|---|---|---|---|
|ACTIVATION_KEY|Name of secret which contains subscription activation key|activation-key|false|
|SOURCE_ARTIFACT|The Trusted Artifact URI pointing to the artifact with the application source code.||true|
|caTrustConfigMapKey|The name of the key in the ConfigMap that contains the CA bundle data.|ca-bundle.crt|false|
|caTrustConfigMapName|The name of the ConfigMap to read CA bundle data from.|trusted-ca|false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ spec:

[available configuration parameters]: https://github.com/containerbuildsystem/cachi2?tab=readme-ov-file#available-configuration-parameters
params:
- name: ACTIVATION_KEY
description: Name of secret which contains subscription activation key
type: string
default: activation-key
- name: SOURCE_ARTIFACT
description: The Trusted Artifact URI pointing to the artifact with
the application source code.
Expand Down Expand Up @@ -79,8 +83,16 @@ spec:
the application source code.
type: string
volumes:
- name: activation-key
secret:
optional: true
secretName: $(params.ACTIVATION_KEY)
- name: config
emptyDir: {}
- name: etc-pki-entitlement
emptyDir: {}
- name: shared
emptyDir: {}
- name: trusted-ca
configMap:
items:
Expand Down Expand Up @@ -110,6 +122,8 @@ spec:
volumeMounts:
- mountPath: /mnt/config
name: config
- mountPath: /shared
name: shared
- mountPath: /var/workdir
name: workdir
steps:
Expand Down Expand Up @@ -143,15 +157,159 @@ spec:
# https://github.com/containerbuildsystem/cachi2/issues/577
yq 'del(.goproxy_url)' <<<"${CONFIG_FILE_CONTENT}" >/mnt/config/config.yaml
fi
- name: check-prefetch-input
image: quay.io/redhat-appstudio/cachi2:0.15.0@sha256:b141cb5cf4d98e6c5f668f1fe172e1d68f2a44ac1027403fbcff94ce1e68185d
env:
- name: INPUT
value: $(params.input)
script: |
if [ -z "${INPUT}" ]; then
# Confirm input was provided though it's likely the whole task would be skipped if it wasn't
echo "No prefetch will be performed because no input was provided for cachi2 fetch-deps"
echo "skip" >/shared/skip
fi
brianwcook marked this conversation as resolved.
Show resolved Hide resolved
- name: register-red-hat
image: quay.io/redhat-appstudio/cachi2:0.15.0@sha256:b141cb5cf4d98e6c5f668f1fe172e1d68f2a44ac1027403fbcff94ce1e68185d
results:
- name: registered
type: string
volumeMounts:
- mountPath: /activation-key
name: activation-key
env:
- name: INPUT
value: $(params.input)
- name: ACTIVATION_KEY
value: $(params.ACTIVATION_KEY)
script: |
#!/bin/bash
if [ -f /shared/skip ]; then
echo "Skipping."
exit 0
fi
brianwcook marked this conversation as resolved.
Show resolved Hide resolved

echo "false" >/shared/registered
ACTIVATION_KEY_PATH="/activation-key"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noob question - how is the ACTIVATION_KEY parameter even used, it doesn't seem to since you're hardcoding the volume path here? What if someone changes the value, how do we probe the right file system location? I'm clearly missing something here making me confused.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path is hardcoded but the secret that is populated to that path has a default value of 'activation-key' in the task parameter declarations and can be overridden in the params passed to task invocation. It is the secret name the user needs to be concerned with, the path is an internal implementation detail.


mkdir -p /shared/rhsm/entitlement
mkdir -p /shared/rhsm/consumer

if [ -e /activation-key/org ]; then
cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to copy the data? Why not reading it directly from the volume

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be possible to read it directly from the volume but I tried to keep this code in sync with the buildah task which does a copy.


echo "Registering with Red Hat subscription manager."
subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)"

# copy generated certificates to /shared/rhsm
cp /etc/pki/entitlement/*.pem /shared/rhsm/entitlement/
cp /etc/pki/consumer/*.pem /shared/rhsm/consumer/

file="$(find /shared/rhsm/entitlement -regextype egrep -regex '.*[0-9]+\.pem' -printf %f)"
echo "file: $file"
basename "$file" .pem >/shared/RHSM_ID
echo "./RHSM_ID:"
cat /shared/RHSM_ID
Comment on lines +207 to +211
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is IMO not needed at all, IIUC you only added it to report the RHSM_ID in the logs, but I wonder what value that brings compared to reporting the TLS auth credentials as part of cachi2's own debug logging system.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

208, 210 and 211 for visibility and we could remove them but it would make any necessary debugging harder. 207 and 209 are crucial functionality.

the "RHSM_ID" is not known before subsription-manager register is run.
That is why the code has to pase this value from the file names. Those files also don't exist before subscrip-manager-register is run. What is happening (and there is no alternative) is this

  1. user provides org id and activation key values in Kube secret
  2. code runs subscription-manager register --org [org] --key key
  3. Two files are generated during registration, [rhsm_id.pem] and `[rhsm_id].key
  4. prefetch input has to be modified on the fly adding certificate options with those file namesfor all the occurences of the rpm manager in the prefetch input - even if all the user passed as input is rpm.

Copy link
Contributor

@eskultety eskultety Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the "RHSM_ID" is not known before subsription-manager register is run. That is why the code has to pase this value from the file names. Those files also don't exist before subscrip-manager-register is run. What is happening (and there is no alternative) is this

1. user provides org id and activation key values in Kube secret

2. code runs subscription-manager register --org [org] --key key

3. Two files are generated during registration,  `[rhsm_id.pem]` and `[rhsm_id].key

4. prefetch input has to be modified on the fly adding certificate options with those file namesfor _all_ the occurences of the rpm manager in the prefetch input - even if all the user passed as input is `rpm`.

We can extract the file names on demand right before executing cachi2. The intermediate RHSM_ID handling is just opaque and makes it visibly hard harder to read, why complicating stuff? :)

Copy link
Contributor Author

@brianwcook brianwcook Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you cannot extract the names only once, modify the input in a separate task and omit passing the names between steps.

Passing data between steps by writing to an emptyDir is a common pattern to save Tekton results space. It is not unexpected in this context or abnormally complicated. If you want to avoid it for some reason then we have to extract the names from the files twice. In any case, I don't think the difference between the two approaches is worth the time spent discussing it and I find parsing the name once to be the simpler way (hence the way I went).


# trust the CA used for Red Hat CDN
cp /etc/rhsm-host/ca/redhat-uep.pem /shared/rhsm/redhat-uep.pem
fi
- name: preprocess-input
image: quay.io/redhat-appstudio/cachi2:0.15.0@sha256:b141cb5cf4d98e6c5f668f1fe172e1d68f2a44ac1027403fbcff94ce1e68185d
args:
- $(params.input)
env:
- name: INPUT
value: $(params.input)
- name: ACTIVATION_KEY
value: $(params.ACTIVATION_KEY)
script: |
#!/bin/python3
import json
import os
import sys


def string_to_json(input: str):
if input in ['bundler', 'generic', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn']:
input = '{"type": "%s"}' % input
print("json: %s" % input)
return input


def json_to_list(input: str):
input = json.loads(input)
if type(input) is dict:
input = [input]
return json.dumps(input)


def inject_certs(input: str, rhsm_id: str):
input_list: list = json.loads(input)

cert = ("/shared/rhsm/entitlement/%s.pem" % rhsm_id)
key = ("/shared/rhsm/entitlement/%s-key.pem" % rhsm_id)
Comment on lines +249 to +250
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your code already assumes there's only ever going to be a single set of entitlement key-cert pair of files which is a reasonable assumption ATM, so you don't really need this RHSM_ID filename construction logic, one file is going to be named xyz-key.pem the other xyz.pem no RHSM involvement needed at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The construction is necessary. Even though the code limits the user to one set of certificates, the certificate names are not known until subscription-manager register is run.

Copy link
Contributor

@eskultety eskultety Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The construction is necessary. Even though the code limits the user to one set of certificates, the certificate names are not known until subscription-manager register is run.

It's true that you don't know the actual names, but I don't believe the construction really is necessary, regardless of whether this is Bash or Python, you can extract the right names dynamically without knowing RHSM_ID at the time of use, because you have the most important bit of information - the files are named the same sans the -key suffix.
What I'm trying to say is, we should only be using and passing around data what we absolutely have to and need instead of generating/passing around data we don't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...but the names of the keys need to be input into the python step so it can be injected into input JSON.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? IIUC the shared volume entitlementdirectory contains only 2 files from which you can derive the intent of each of those.

Copy link
Contributor Author

@brianwcook brianwcook Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the user input is simply rpm and an activation key is presented, the input string has to be transformed before being passed to cachi2 to:

[{"type": "rpm", "options": {"ssl": {"client_key": null, "client_cert": null, "ca_bundle": null, "verify": 1}}}]

And then the client_key and client_cert values need to be populated with the name of the pem file generated by subscription-manager register

ca_bundle = os.getenv("CA_BUNDLE", None)
for pkg_man in input_list:
if pkg_man["type"] == "rpm":

# preserve verify setting
verify = \
pkg_man.get("options", {}).get("ssl", {}).get("ssl_verify", 1)

# preserve other options
options: dict = pkg_man.get('options', {})

ssl_options = {
"client_key": key,
"client_cert": cert,
"ca_bundle": ca_bundle,
"ssl_verify": verify}

options['ssl'] = ssl_options
pkg_man["options"] = options
return (json.dumps(input_list))


def convert_input(input, rhsm_id):
input = string_to_json(input)
input = json_to_list(input)
input = inject_certs(input, rhsm_id)
return input


if __name__ == '__main__':

if os.path.isfile("/shared/skip"):
sys.exit()

rhsm_id = ""
input = ""

try:
f = open("/shared/RHSM_ID", "r")
rhsm_id = f.read().strip("\n")
except FileNotFoundError:
print("No RHSM ID found.")

if rhsm_id == "":
input = sys.argv[1]
else:
print("RHSM Cert ID is: %s" % rhsm_id)
print("Called with args: %s" % str(sys.argv))
input = convert_input(sys.argv[1], rhsm_id)

print("Preprocessing result: %s" % input)
with open('/shared/rhsm/preprocessed_input', 'w') as f:
f.write(input)
- name: prefetch-dependencies
image: quay.io/redhat-appstudio/cachi2:0.15.0@sha256:b141cb5cf4d98e6c5f668f1fe172e1d68f2a44ac1027403fbcff94ce1e68185d
volumeMounts:
- mountPath: /mnt/trusted-ca
name: trusted-ca
readOnly: true
- mountPath: /activation-key
name: activation-key
env:
- name: INPUT
value: $(params.input)
- name: DEV_PACKAGE_MANAGERS
value: $(params.dev-package-managers)
- name: LOG_LEVEL
Expand All @@ -165,9 +323,10 @@ spec:
- name: WORKSPACE_NETRC_PATH
value: $(workspaces.netrc.path)
script: |
if [ -z "${INPUT}" ]; then
# Confirm input was provided though it's likely the whole task would be skipped if it wasn't
echo "No prefetch will be performed because no input was provided for cachi2 fetch-deps"
#!/bin/bash

if [ -f /shared/skip ]; then
echo "Skipping."
exit 0
fi

Expand All @@ -183,6 +342,16 @@ spec:
dev_pacman_flag=""
fi

INPUT=$(cat /shared/rhsm/preprocessed_input)
export INPUT

# trust Red Hat CA cert used for Red Hat CDN
if [ -f /shared/rhsm/redhat-uep.pem ]; then
echo "Adding Red Hat CA certificate to trusted roots."
cp /shared/rhsm/redhat-uep.pem /etc/pki/ca-trust/source/anchors/
update-ca-trust
fi

# Copied from https://github.com/konflux-ci/build-definitions/blob/main/task/git-clone/0.1/git-clone.yaml
if [ "${WORKSPACE_GIT_AUTH_BOUND}" = "true" ]; then
if [ -f "${WORKSPACE_GIT_AUTH_PATH}/.git-credentials" ] && [ -f "${WORKSPACE_GIT_AUTH_PATH}/.gitconfig" ]; then
Expand Down Expand Up @@ -225,6 +394,18 @@ spec:

cachi2 --log-level="$LOG_LEVEL" inject-files /var/workdir/cachi2/output \
--for-output-dir=/cachi2/output
- name: unregister-rhsm
image: quay.io/redhat-appstudio/cachi2:0.15.0@sha256:b141cb5cf4d98e6c5f668f1fe172e1d68f2a44ac1027403fbcff94ce1e68185d
script: |
#!/bin/bash
if [ -f /shared/skip ]; then
echo "Skipping."
exit 0
fi

cp /shared/rhsm/consumer/* /etc/pki/consumer/
cp /shared/rhsm/entitlement/* /etc/pki/entitlement/
subscription-manager unregister || true
- name: create-trusted-artifact
image: quay.io/redhat-appstudio/build-trusted-artifacts:latest@sha256:81c4864dae6bb11595f657be887e205262e70086a05ed16ada827fd6391926ac
args:
Expand Down
14 changes: 8 additions & 6 deletions task/prefetch-dependencies/0.1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ See docs at https://github.com/containerbuildsystem/cachi2#basic-usage.

## Configuration

Config file must be passed as a YAML string. For all available config options please check [available configuration parameters] page.
Config file must be passed as a YAML string. For all available config options please check
[available configuration parameters] page.

Example of setting timeouts:

```yaml
params:
- name: config-file-content
value: |
---
requests_timeout: 300
subprocess_timeout: 3600
- name: config-file-content
value: |
---
requests_timeout: 300
subprocess_timeout: 3600
```
[available configuration parameters]: https://github.com/containerbuildsystem/cachi2?tab=readme-ov-file#available-configuration-parameters
Expand All @@ -29,6 +30,7 @@ params:
|config-file-content|Pass configuration to cachi2. Note this needs to be passed as a YAML-formatted config dump, not as a file path! |""|false|
|caTrustConfigMapName|The name of the ConfigMap to read CA bundle data from.|trusted-ca|false|
|caTrustConfigMapKey|The name of the key in the ConfigMap that contains the CA bundle data.|ca-bundle.crt|false|
|ACTIVATION_KEY|Name of secret which contains subscription activation key|activation-key|false|
## Workspaces
|name|description|optional|
Expand Down
Loading
Loading