Skip to content

Custom improvements for OpenShift#825

Merged
knative-prow-robot merged 4 commits intoknative:mainfrom
matejvasek:openshirt-specifing-init
Feb 22, 2022
Merged

Custom improvements for OpenShift#825
knative-prow-robot merged 4 commits intoknative:mainfrom
matejvasek:openshirt-specifing-init

Conversation

@matejvasek
Copy link
Copy Markdown
Contributor

@matejvasek matejvasek commented Feb 9, 2022

🎁 add initial framework for cluster flavor detection/actions

Detects when deploying to OpenShift and use internal registry

@knative-prow-robot knative-prow-robot added do-not-merge/work-in-progress 🤖 PR should not merge because it is a work in progress. approved 🤖 PR has been approved by an approver from all required OWNERS files. size/L 🤖 PR changes 100-499 lines, ignoring generated files. labels Feb 9, 2022
@matejvasek
Copy link
Copy Markdown
Contributor Author

@salaboy @shinigambit would you be OK with this?

@matejvasek matejvasek requested a review from lkingland February 9, 2022 18:17
@matejvasek
Copy link
Copy Markdown
Contributor Author

Just draft for now.

@matejvasek matejvasek requested a review from lance February 9, 2022 18:17
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 9, 2022

Codecov Report

Merging #825 (04354ae) into main (64091fb) will decrease coverage by 0.48%.
The diff coverage is 20.58%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #825      +/-   ##
==========================================
- Coverage   43.36%   42.88%   -0.49%     
==========================================
  Files          48       49       +1     
  Lines        4690     4783      +93     
==========================================
+ Hits         2034     2051      +17     
- Misses       2367     2445      +78     
+ Partials      289      287       -2     
Impacted Files Coverage Δ
http/transport.go 63.15% <0.00%> (ø)
openshift/openshift.go 6.41% <6.41%> (ø)
cmd/client.go 16.39% <58.82%> (+16.39%) ⬆️
cmd/build.go 59.63% <100.00%> (ø)
cmd/deploy.go 16.41% <100.00%> (ø)
cmd/root.go 69.44% <100.00%> (+1.38%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 64091fb...04354ae. Read the comment docs.

@matejvasek
Copy link
Copy Markdown
Contributor Author

@lance @lkingland with this we could just patch tags in Makefile in midstream.

@evankanderson
Copy link
Copy Markdown
Member

Would it make sense to contribute this to https://github.com/google/go-containerregistry/tree/main/pkg/authn, and leverage that library? That library seems to be used by a lot of components, including the pack CLI.

@evankanderson
Copy link
Copy Markdown
Member

In particular, there's a credential helper protocol that would allow you to specify an external binary helper and authorization config for image-registry.openshift-image-registry.svc like so:

{
	"credHelpers": {
		"image-registry.openshift-image-registry.svc": "os",
	}
}

This would also work with other commands like crane, dive and docker, assuming you had docker-credential-os in your path.

@matejvasek
Copy link
Copy Markdown
Contributor Author

@evankanderson I am not sure what you mean specifically. We are using go-containerregistry and credsHelpers internally for credentials handling.

Here I am just adding very specialized loader func GetDockerCredentialLoaders() []creds.CredentialsCallback {} reason:
Internal OpenShift registry has the same credentials as the k8s. So this specialized call pulls credentials from ~/.kube/config instead form ~/.config/func/auth.json.

@evankanderson
Copy link
Copy Markdown
Member

I'm suggesting: why not have a docker-credential-openshift or docker-credential-k8s that extracts those credentials, and can use the standard credsHelper mechanism?

@matejvasek
Copy link
Copy Markdown
Contributor Author

matejvasek commented Feb 10, 2022

This would also work with other commands like crane, dive and docker, assuming you had docker-credential-os in your path.

We are deliberately using ~/.docker/config.json as read-only, so if somebody is already logged into registry via docker CLI, the func should use that. Opposite is not true: if you are logged via func the docker CLI won't see the credentials.
EDIT: this is not entirely true: while func has private auth file, it still can be "connected" to docker via helper (e.g. kernel keychaing).

@matejvasek
Copy link
Copy Markdown
Contributor Author

matejvasek commented Feb 10, 2022

I'm suggesting: why not have a docker-credential-openshift or docker-credential-k8s that extracts those credentials, and can use the standard credsHelper mechanism?

I see. Interesting idea! I like it. However this would require user to download and put such a helper onto PATH. I wish everything worked automagically here: If cluster is OpenShift then internal registry is used by default without needing any additional steps.
Ideally user shouldn't even notice there is something like image registry.

@matejvasek
Copy link
Copy Markdown
Contributor Author

matejvasek commented Feb 11, 2022

@lance @lkingland @zroubalik If I understand it correctly @salaboy and @evankanderson suggest not to do this by direct integration in binary.

Instead this behaviour could be customized trough some local files.

What would it mean for us (RH):
We would need to create some (post)installation script that would create such a files.

Such a script might need to:

  • install custom credential helper binary, and put it into path via append to shell env/rc files
  • predefine credentials in ~/.config/func/auth.json
  • pull the OSh service CA and save it on FS
  • create some configuration file that would say the OSh service CA is trusted for some specific hosts
  • set export FUNC_REGISTRY envvar via append to shell env/rc files

Also there probably would be different installation scripts for each OS.

@evankanderson
Copy link
Copy Markdown
Member

I'm not sure what tools users are already using; I see that there's already an oc CLI that suggests installing a binary and moving in onto your PATH. The same will need to be true for the func CLI -- at some point, you might want to have a script that copies things into PATH, but having oc and docker-credential-oc seems like it would enable using the full OCI ecosystem with OpenShift.

I'm not blocking this submit, just trying to suggest ways to make the overall OpenShift experience align with the rest of the ecosystem, which I think helps us all.

@evankanderson
Copy link
Copy Markdown
Member

Depending on the space in the oc binary, you could follow the gcloud example, and have a command to update the dockerconfig.

@lance
Copy link
Copy Markdown
Contributor

lance commented Feb 11, 2022

@lance @lkingland @zroubalik If I understand it correctly @salaboy and @evankanderson suggest not to do this by direct integration in binary.

Why not do this in openshift-knative/kn-plugin-func?

@matejvasek
Copy link
Copy Markdown
Contributor Author

@lance @lkingland @zroubalik If I understand it correctly @salaboy and @evankanderson suggest not to do this by direct integration in binary.

Why not do this in openshift-knative/kn-plugin-func?

yes we can do it by git am patch

@matejvasek
Copy link
Copy Markdown
Contributor Author

@lance @lkingland @zroubalik If I understand it correctly @salaboy and @evankanderson suggest not to do this by direct integration in binary.

Why not do this in openshift-knative/kn-plugin-func?

yes we can do it by git am patch

But I strongly prefer not to have custom patches in openshift-knative/kn-plugin-func, in long term it will complicate maintenance.

@lance
Copy link
Copy Markdown
Contributor

lance commented Feb 11, 2022

But I strongly prefer not to have custom patches in openshift-knative/kn-plugin-func, in long term it will complicate maintenance.

I agree with you there. This is the more desirable way for RHT, to be sure. Would other vendor offered clusters benefit from something similar?

@salaboy
Copy link
Copy Markdown
Contributor

salaboy commented Feb 12, 2022

@lance I think this will happen for sure.. other vendors will need their own specific hooks and checks, so I will tend to agree with @evankanderson in the sense of let's make sure that we make func and other libraries better so we can support multiple Kubernetes flavours.

I wouldn't like it either if you need to do patches to create your specific binaries, so my suggestion would be to try to create the mechanisms to check if you are running on Openshift as generic as possible.

All these check can conform a profile, for example if check A, B and C are true, you are in Openshift, if only check A and C are true, you are in another vendor. If we follow the approach implemented in this PR, then each vendor will need to have it's own set of checks and we will not be promoting people to reuse those mechanisms. Just to be clear, these checks can definitely be in the binary, or contributed to the downstream libraries as @evankanderson mentioned.

@lkingland
Copy link
Copy Markdown
Member

lkingland commented Feb 12, 2022

This is a valid, and useful question. I believe the current core library structure properly handles the situation using a fairly standard mechanism: The core func library is essentially dependency-free and agnostic of cluster flavor. Third-party-specifics are held behind interfaces. main is responsible for introspecting the environment, either automatically or explicitly by the user, to load the core library (logic) with the correct implementation. For example, there may be a different "deployer" if someone is deploying to a local Kind cluster, an EC2 cluster, an OpenShift cluster etc. These nuanced implementations reside in packages kind, openshift, ec2, etc. Therefore, I do have a suspicion we could be more flexible here than use build flags, allowing a single binary to be able to use any properly configured cluster regardless of its flavor

Comment on lines +1 to +2
//go:build !openshift
// +build !openshift
Copy link
Copy Markdown
Member

@lkingland lkingland Feb 12, 2022

Choose a reason for hiding this comment

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

I am happy to see that this logic is in the right location: cmd with a reference to an implementation-specific package. This is the same as can be used for all future cases where flavor-specific code is necessary, not just for OpenShift.

My concern with this approach is that it is calculated at build-time and thus results in a binary that is OpenShift specific. This is not, I think, a necessary capitulation, because the core func logic is agnostic if cluster flavor.

Is there not a way we can detect the cluster flavor at runtime?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 here.. I would be more in favour of runtime detection with "standard" mechanisms, that we can reuse for other environments.

@evankanderson
Copy link
Copy Markdown
Member

VMware actually has a slightly more complicated detection logic, because "Tanzu Application Platform" is a layer which can be applied to different types of clusters, including Tanzu Kubernetes Grid (VMware), EKS (Amazon), or even OpenShift on AWS (turtles all the way down).

Should this check be conditioned on the cluster, the registry, or some combination?

We might be able to use the auth preflight check with the Server header to give the different auth plugins a chance to update the client with auth credentials.

@lkingland
Copy link
Copy Markdown
Member

lkingland commented Feb 14, 2022

Should this check be conditioned on the cluster, the registry, or some combination?
...
We might be able to use the auth preflight check with the Server header

If we could use that same request to find out if it is Tanzu Application Platform, OpenShift, or vanilla; that seems like it could work well. The analogy perhaps being this information is a bit like operating system, where Function is to Kubernetes Flavor as Binary is to Operating System.

Am I understanding this correctly? By default the "Deployer" used by func is a vanilla Kubernetes deployer which requires that a registry be provided and that Knative is active in the cluster. During the authentication phaze we detect that the Cluster is a... customized distribution, and if there exists an integration, a distribution-specific Deployer is loaded. In the case of OpenShift, this Deployer utilizes the internal registry by default, treating a user-provided registry not as required, but as an optional override.

Using the Server header in the auth preflight is runtime, and done for every cluster connection of course, so it would accomplish the goal of allowing func to swap out any of the concrete implementations used by Client as necessary to take advantage of flavor-specific advantages. It also accomplishes the goal of having a single func binary capable of interacting with any cluster which meets the minimum requirements (Knative installed), but with flavor-specific benefits.

Sounds like a good topic for this week's call.

@knative-prow-robot knative-prow-robot removed the do-not-merge/work-in-progress 🤖 PR should not merge because it is a work in progress. label Feb 16, 2022
var additionalCredLoaders []creds.CredentialsCallback

switch {
case openshift.IsOpenShift():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just a comment:
I can see a future where we have a set of supported platforms, which are each given the opportunity to self-identify and add to the features offered by the Function Client. This would work a bit like a connection upgrade attempt. This keeps the core library clean of any third-party dependencies, but allows for our Functions library to offer the ability to seamelssly switch between cluster flavors.

Copy link
Copy Markdown
Member

@lkingland lkingland left a comment

Choose a reason for hiding this comment

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

Looking good!
I am sure the exact way that we implement support for various Kubernetes flavors will evolve over time. For now, it is nice to see that a single func will work seamlessly whether it is connected to a vanilla K8S cluster provider or an OpenShift cluster provider; transparently making use of any upgrades/preconfigurations provided by a supported provider.

@knative-prow-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: lkingland, matejvasek, zroubalik

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [lkingland,matejvasek,zroubalik]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@salaboy
Copy link
Copy Markdown
Contributor

salaboy commented Feb 18, 2022

This looks like a good first iteration. I do feel that we haven't answered @evankanderson question yet: Should this check be conditioned on the cluster, the registry, or some combination? Right now these checks are performed by default, am I right?

@matejvasek
Copy link
Copy Markdown
Contributor Author

Should this check be conditioned on the cluster, the registry, or some combination?

We are conditioning on cluster type (by checking that the cluster contains something that is own to OpenShift).

@matejvasek
Copy link
Copy Markdown
Contributor Author

@lance @lkingland @salaboy @evankanderson are we OK with merging this into main?

Copy link
Copy Markdown
Contributor

@lance lance left a comment

Choose a reason for hiding this comment

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

This in general looks good to me, @matejvasek and I am OK merging it. However, since I have not actively been engaged in the dialog I'd like at least @salaboy and @lkingland or @zroubalik to also give the OK. So, I'm not removing hold just yet.

/lgtm

@lkingland
Copy link
Copy Markdown
Member

@lance @lkingland @salaboy @evankanderson are we OK with merging this into main?

Yes @matejvasek I think this is fine to merge.

Copy link
Copy Markdown
Member

@evankanderson evankanderson left a comment

Choose a reason for hiding this comment

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

A few code review comments.

I'd like to see somewhat better coverage of the new package, but I understand that we'd like to bake this in rather than fooling around with credential helpers.

Comment on lines +35 to +41
cfgMapName := "service-ca-config-" + rand.String(5)

cfgMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: cfgMapName,
Annotations: map[string]string{"service.beta.openshift.io/inject-cabundle": "true"},
},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why not use generateName here?

This also feels like maybe this function should have a comment explaining what it does and why this is a safe way to get the CA cert.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(I think you're avoiding generateName because you want to simplify deleting this shortly after creation)

Copy link
Copy Markdown
Contributor Author

@matejvasek matejvasek Feb 22, 2022

Choose a reason for hiding this comment

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

I am using locally generated name so I can set up Watch before creation. Is that all right @evankanderson ?

return nil
}

var selectCA func(ctx context.Context, serverName string) (*x509.Certificate, error)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like it's finding the specific cert, not necessarily the CA for the cert (i.e. if you have a root CA that signs the registry cert, this will find the registry cert, not the root CA). That's okay, but we might prefer to call this registryCert.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's CA for multiple services provided by OpenShift, but here for now are are enabling trust only for registry. I don't feel confident enabling the CA for any and all connections.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@evankanderson it should find self-signed CA -- authority not just certificate.

isOpenShift = false
return
}
_, err = client.CoreV1().Services("openshift-image-registry").Get(context.TODO(), "image-registry", metav1.GetOptions{})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm -- this will return false if the user doesn't have services get permission on the openshift-image-registry namespace.

I'm not sure if there's another / better way to detect this, either from server headers or by probing the registry and looking at its header / challenge. I think go-containerregistry uses a GET /v2/ HTTP /1.1 probe of the registry to figure out what and whether auth is required.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

https://github.com/google/go-containerregistry/tree/main/pkg/authn#the-registry suggests looking at the Www-Authenticate header for clues as to the authentication mechanism.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would avoid doing HTTP request to internal service here -- it requires some setup I don't want to do here.

Copy link
Copy Markdown
Contributor Author

@matejvasek matejvasek Feb 22, 2022

Choose a reason for hiding this comment

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

Hmm -- this will return false if the user doesn't have services get permission on the openshift-image-registry namespace.

@evankanderson This does work with non-privileged user. Non-privilieged user will get different errors for non-existent service and for service that is not just permitted (NotFound vs. PermissionDenied).

Signed-off-by: Matej Vasek <mvasek@redhat.com>
@matejvasek matejvasek force-pushed the openshirt-specifing-init branch from eea9ac0 to 5b15436 Compare February 22, 2022 13:57
@knative-prow-robot knative-prow-robot removed the lgtm 🤖 PR is ready to be merged. label Feb 22, 2022
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
@matejvasek
Copy link
Copy Markdown
Contributor Author

matejvasek commented Feb 22, 2022

I'd like to see somewhat better coverage of the new package

Unfortunately good integration test here require OpenShift cluster that in not running in GH action.
So some of the code will be covered in our CI.

@matejvasek
Copy link
Copy Markdown
Contributor Author

@lance @lkingland please lgtm

Signed-off-by: Matej Vasek <mvasek@redhat.com>
@lance
Copy link
Copy Markdown
Contributor

lance commented Feb 22, 2022

/lgtm

@knative-prow-robot knative-prow-robot added the lgtm 🤖 PR is ready to be merged. label Feb 22, 2022
@matejvasek
Copy link
Copy Markdown
Contributor Author

/unhold

@knative-prow-robot knative-prow-robot removed the do-not-merge/hold 🤖 PR should not merge because someone has issued a /hold command. label Feb 22, 2022
@knative-prow-robot knative-prow-robot merged commit 4fec4af into knative:main Feb 22, 2022
This was referenced Feb 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved 🤖 PR has been approved by an approver from all required OWNERS files. lgtm 🤖 PR is ready to be merged. size/L 🤖 PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants