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

Store the container-machine mapping predictably #13858

Merged
merged 7 commits into from
Jul 24, 2019

Conversation

metlos
Copy link
Contributor

@metlos metlos commented Jul 16, 2019

What does this PR do?

Store the container-machine mapping using a pair of annotations with a
predictable name length to prevent breaking the 63 character limit on the
k8s annotation names.

What issues does this PR fix or reference?

#13303

predictable name length to prevent breaking the 63 character limit on the
k8s annotation names.

Signed-off-by: Lukas Krejci <lkrejci@redhat.com>
@tsmaeder
Copy link
Contributor

What makes the machine name size predictable? To me it looks like it's still a function of the name of the container.

@metlos
Copy link
Contributor Author

metlos commented Jul 16, 2019

It's not the machine name that has a predictable size - as you correctly note, it is a function of the container name.

Before, we stored the mapping from the container name to the machine name using a single annotation that included the container name in its key. E.g.:

org.eclipse.che.container.vscode-kubernetes-tools.machine_name=blah

As you see above, the length of the annotation key is unpredictable and depends on the name of the container.

This PR changes the mapping into 2 annotations:

che.container.1.name=vscode-kubernetes-tools
che.container.1.machine=blah

And contains additional logic to increment the "1" in the above names for additional mappings if the pod contains more containers.

The length of the annotation keys is thus predictable and it is hard to imagine it going past the 63 character limit because that would require there to be more than 10^43 containers in a single pod.

Also the PR changes the code in all the places that used to read the machine name from the annotations directly to delegate that to the methods in Names so that the above more complex logic is applied.

Signed-off-by: Lukas Krejci <lkrejci@redhat.com>
@metlos
Copy link
Contributor Author

metlos commented Jul 16, 2019

ci-test

@che-bot
Copy link
Contributor

che-bot commented Jul 16, 2019

Results of automated E2E tests of Eclipse Che Multiuser on OCP:
Build details
Test report
docker image: eclipseche/che-server:13858
https://github.com/orgs/eclipse/teams/eclipse-che-qa please check this report.

Copy link
Contributor

@amisevsk amisevsk left a comment

Choose a reason for hiding this comment

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

I'm personally not a huge fan of this approach -- it adds a fair bit of complexity and looks like a hack at the end of the day.

If the machine name annotation is never used, then why don't we remove it? It makes more sense to add the logic for how this annotating is done once something actually needs to use it.

Alternatively, changing MACHINE_NAME_ANNOTATION_FORMAT to org.eclipse.che.machine-name/%s would solve the problem for any container with a name less than 63 characters since everything before / is a prefix.

Added a few optional suggestions.

@davidfestal Just to double check but does this affect your operator work?

return null;
}

for (Map.Entry<String, String> e : annotations.entrySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could likely be made a lot simpler and more readable in a stream:

    annotations.entrySet()
        .stream()
        .filter(e -> e.getKey().startsWith(CONTAINER_META_PREFIX))
        .filter(e -> e.getKey().endsWith(CONTAINER_META_NAME_SUFFIX))
        .filter(e -> containerName.equals(e.getValue()))
        .map(...)
        .collect(...)


String index =
key.substring(
CONTAINER_META_PREFIX.length(), key.length() - CONTAINER_META_NAME_SUFFIX.length());
Copy link
Contributor

Choose a reason for hiding this comment

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

A cleaner way to do this would be to match it out using a regex based off of prefix and suffix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Call me old school, but I fail to see how is that cleaner... If you insist, I can change it, but substring is IMHO both faster and functionally simpler than regex matching.


private static final String CONTAINER_META_PREFIX = "che.container.";
private static final String CONTAINER_META_NAME_SUFFIX = ".name";
private static final String CONTAINER_META_MACHINE_SUFFIX = ".machine";
Copy link
Contributor

Choose a reason for hiding this comment

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

To me it looks better if you use

CONTAINER_META_NAME = "che.container.%s.name";
CONTAINER_META_MACHINE = "che.contianer.%s.machine";

an then String.format() the indexes in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the casual Google search, format is at least an order of magnitude slower than concatenation. I would 100% agree with you if these were localizable strings, but they're not.

return max;
}

for (String key : annotations.keySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could also be a stream operation, something like:

annotations.keySet()
    .stream()
    .mapToInt()
    .max()

@davidfestal
Copy link
Contributor

@davidfestal Just to double check but does this affect your operator work?

I also don't like the numbering very much. That means that you have to know the final number of containers at start, before generating the deployment annotations but obviously after doing brokering. In the Workspace CRD controller POC that's not the case for example. We add containers in the overall workspace deployment PodSpec as soon as a plugin component is found in the devfile and brokering is called on-the-fly in the controller itself for this component.

I really like better the alternate approach @amisevsk proposed:

changing MACHINE_NAME_ANNOTATION_FORMAT to org.eclipse.che.machine-name/%s

Copy link
Member

@sleshchenko sleshchenko left a comment

Choose a reason for hiding this comment

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

Have you investigated the possibility of getting rid of a machine name in annotations?

Generally, I'm OK with the proposed solution since it solves an issue and looks like we would be able to get rid of this quite complex logic along with WorkspaceConfig format (maybe current Runtime model)

Comment about org.eclipse.che.machine-name/%s:
org.eclipse.che.machine-name/ has 29 characters and it would mean that we should limit machine name to 34 characters and everything that propagated as machine name, like alias of dockerimage component, also we should introduce algorithm of how we transform image of dockerimage component to machine name (now it does not have limit of characters). Machine name concept is a core concept and no one knows every place that is affected by this. I think it can be investigated more, and it's not equivalent to the current approach that allows using unlimited (quite long) machine name.

if (annotations != null
&& (machineName = annotations.get(format(MACHINE_NAME_ANNOTATION_FMT, containerName)))
!= null) {
if ((machineName = findMachineName(annotations, containerName)) != null) {
Copy link
Member

Choose a reason for hiding this comment

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

Please consider updating of java docs as well.
I don't see any java docs that would explain how we store machines names in annotations. I guess, previously java doc of MACHINE_NAME_ANNOTATION_FMT const has some short info.

@@ -98,4 +162,83 @@ public static String uniqueResourceName(String originalResourceName, String work
public static String generateName(String prefix) {
return NameGenerator.generate(prefix, GENERATED_PART_SIZE);
}

private static String findMachineName(Map<String, String> annotations, String containerName) {
Copy link
Member

Choose a reason for hiding this comment

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

Please add @nullable

for (Map.Entry<String, String> e : annotations.entrySet()) {
String key = e.getKey();

if (!key.startsWith(CONTAINER_META_PREFIX)) {
Copy link
Member

Choose a reason for hiding this comment

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

I see quite the same logic in findContainerIndex method, maybe we can rewrite this method in the following way to reduce the same code:

  @Nullable
  private static String findMachineName(Map<String, String> annotations, String containerName) {
    int index = findContainerIndex(annotations, containerName);
    if (index < 0) {
      return null;
    }

    String machineNameKey = CONTAINER_META_PREFIX + index + CONTAINER_META_MACHINE_SUFFIX;
    return annotations.get(machineNameKey);
  }

}

for (String key : annotations.keySet()) {
if (!(key.startsWith(CONTAINER_META_PREFIX) && key.endsWith(CONTAINER_META_NAME_SUFFIX))) {
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about using Pattern instead of startsWith+endsWith+substring?
Like the following
https://gist.github.com/sleshchenko/f2190306fdd7c9dc2cbb071bc22d64e0

@metlos
Copy link
Contributor Author

metlos commented Jul 18, 2019

tldr; Yes, it is a hack put in place to fix the current wrong behavior with minimal impact on the codebase. The hopes are the need for the mapping goes away once we change the k8s infra internals that are still implemented around the old domain model.

Have you investigated the possibility of getting rid of a machine name in annotations?

The Names.machineName(*) methods have 14 usages all over the k8s infrastructure (and 1 on openshift infra).

I looked at them while investigating this and, mostly, the mapping is used to map the old model of servers/installers/machines that we still use internally to the actual k8s deployment. As such, I decided to not try to get rid of this mapping because it would be too risky at this point in time.

changing MACHINE_NAME_ANNOTATION_FORMAT to org.eclipse.che.machine-name/%s

while this would improve the situation greatly, it still imposes a limit that we don't enforce anywhere. So I chose an approach that doesn't impose that limit again to keep the impact of the change to the minimum. If I merely switched to the format you suggest, I'd have to also modify all the places that result in definition of a machine name (devfile appliers, factory, workspace config, ...) and add the length validation.

That said, if we agree here that such validation wouldn't be needed (to limit the scope of the change), I am more than happy to apply that format. I like it better, too.

That means that you have to know the final number of containers at start, before generating the deployment annotations but obviously after doing brokering.

I believe that's not the case. At least in the current implementation, no outside caller knows anything about any numbering. The numbering is a mere implementation choice of the Names class used to "bind" the two annotations together.

@davidfestal
Copy link
Contributor

That means that you have to know the final number of containers at start, before generating the deployment annotations but obviously after doing brokering.

I believe that's not the case. At least in the current implementation, no outside caller knows anything about any numbering. The numbering is a mere implementation choice of the Names class used to "bind" the two annotations together.

@metlos I'm speaking with the Workspace CRD Operator POC in mind: it creates the various Che workspace K8S resources without using the internal k8s infrastructure implementation. It uses its own logic implementation to provide compatible K8S resources. And in this context, using this type of numbering seems more a backward step than a forward one, and makes it more complicated.

That said, I just wanted to notify you, but that's not a blocker, I'll accommodate.

@metlos
Copy link
Contributor Author

metlos commented Jul 18, 2019

That said, I just wanted to notify you, but that's not a blocker, I'll accommodate.

Ok, I see what you mean now. You want to keep compatibility between workspaces created using the CRD and che server. I didn't take that into account actually. Not sure we need to be backwards compatible here, though. I assume you don't need these annotations in your implementation at all?

@davidfestal
Copy link
Contributor

That said, I just wanted to notify you, but that's not a blocker, I'll accommodate.

Ok, I see what you mean now. You want to keep compatibility between workspaces created using the CRD and che server. I didn't take that into account actually. Not sure we need to be backwards compatible here, though?

For various reason, I think we should keep compatibility between both, at least for a certain amount of time. But as I said, I'll accommodate on the Workspace CRD side if you don't have any better solution for now on your side.

Signed-off-by: Lukas Krejci <lkrejci@redhat.com>
@amisevsk
Copy link
Contributor

@sleshchenko For annotations, a up-to-256-char prefix is permitted (the / delimits this), so it would give the full 63 chars for the machine name. See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set

I agree on it still imposing a limit we don't enforce, however.

…ions-too-long

Signed-off-by: Lukas Krejci <lkrejci@redhat.com>
Signed-off-by: Lukas Krejci <lkrejci@redhat.com>
Signed-off-by: Lukas Krejci <lkrejci@redhat.com>
@metlos
Copy link
Contributor Author

metlos commented Jul 19, 2019

I reviewed all the involved code again and I think we can reasonably assume that the container can always be < 63 chars. The plugins/editors already have that limitation in place (or rather they just trim the name to be < 63 chars) and for dockerimages I added a validation check that will fail the deployment of a devfile and will suggest to adding an alias for such component. The kubernetes/openshift components don't seem to be involved in machine naming at all (or at least they don't manifest themselves in the workspace pod annotations).

Thanks for the suggestion @amisevsk!

Copy link
Contributor

@amisevsk amisevsk left a comment

Choose a reason for hiding this comment

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

LGTM, thanks @metlos !

@@ -257,6 +257,14 @@ static String toMachineName(String imageName) throws DevfileException {
return imageName;
}

if (imageName.length() > Names.MAX_CONTAINER_NAME_LENGTH) {
Copy link
Member

Choose a reason for hiding this comment

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

Isn't it something that can be checked by JSON Schema?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, we can't really restrict the names of the images. What would have to happen is conditional check that would mark alias required if the length of the image name would be > 63. Maybe it is doable in the schema but we have to have the check in the code anyway. So for simplicity's sake I only did it in the code. I believe the PR is writable by contributors so please add the schema changes if required (I don't have access to my computer this week).

Copy link
Member

@sleshchenko sleshchenko left a comment

Choose a reason for hiding this comment

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

LGTM

@metlos
Copy link
Contributor Author

metlos commented Jul 22, 2019

ci-test

@che-bot
Copy link
Contributor

che-bot commented Jul 22, 2019

Results of automated E2E tests of Eclipse Che Multiuser on OCP:
Build details
Test report
docker image: eclipseche/che-server:13858
https://github.com/orgs/eclipse/teams/eclipse-che-qa please check this report.

@metlos
Copy link
Contributor Author

metlos commented Jul 22, 2019

ci-test

@che-bot
Copy link
Contributor

che-bot commented Jul 22, 2019

Results of automated E2E tests of Eclipse Che Multiuser on OCP:
Build details
Test report
docker image: eclipseche/che-server:13858
https://github.com/orgs/eclipse/teams/eclipse-che-qa please check this report.

@sleshchenko
Copy link
Member

I've retest locally the same stack (go) that failed for ci and it works for me. Will try to rerun on ci, maybe it's like a random failure.

@sleshchenko
Copy link
Member

ci-test

@che-bot
Copy link
Contributor

che-bot commented Jul 23, 2019

Results of automated E2E tests of Eclipse Che Multiuser on OCP:
Build details
Test report
docker image: eclipseche/che-server:13858
https://github.com/orgs/eclipse/teams/eclipse-che-qa please check this report.

Copy link
Contributor

@nickboldt nickboldt left a comment

Choose a reason for hiding this comment

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

+0. Others have already +1'd and I'm clearing my PR review backlog. No opinion as I'm not SME here.

Signed-off-by: Sergii Leshchenko <sleshche@redhat.com>
@sleshchenko
Copy link
Member

ci-test

@che-bot
Copy link
Contributor

che-bot commented Jul 24, 2019

Results of automated E2E tests of Eclipse Che Multiuser on OCP:
Build details
Test report
docker image: eclipseche/che-server:13858
https://github.com/orgs/eclipse/teams/eclipse-che-qa please check this report.

@artaleks9
Copy link
Contributor

Selenium tests execution on Eclipse Che Multiuser on OCP (https://ci.codenvycorp.com/job/che-pullrequests-test-ocp/1941/) doesn't show any regression against this Pull Request.

@sleshchenko sleshchenko merged commit e598e22 into eclipse-che:master Jul 24, 2019
@sleshchenko sleshchenko deleted the bug/13303-annotations-too-long branch July 24, 2019 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants