GoogleCloudPlatform / metacontroller

Lightweight Kubernetes controllers as a service
https://metacontroller.app/
Apache License 2.0
792 stars 105 forks source link

CompositeController - Infinite loop reconciling when having multiple volumemounts with same named configmap #182

Open rajasaur opened 5 years ago

rajasaur commented 5 years ago

I have a CompositeController which creates a Deployment type object with volumeMounts from configmap which have the same name but mounted at different locations using subpath. For e.g.

        volumeMounts:
        - mountPath: /var/www/vhosts/XXXX/XXXX/adminwebservice/config/parameters.yml
          name: app-conf
          subPath: adminwebservice_parameters.yml
        - mountPath: /var/www/vhosts/XXXX/XXXX/webservice/config/parameters.yml
          name: app-conf
          subPath: webservice_parameters.yml
        - mountPath: /var/www/vhosts/XXXX/XXXX/frontend/config/parameters.yml
          name: app-conf
          subPath: frontend_parameters.yml
        - mountPath: /var/www/vhosts/XXXX/XXXX/commons/config/parameters.yml
          name: app-conf
          subPath: commons_parameters.yml
        - mountPath: /var/www/vhosts/XXXX/XXXX/commons/config/hosts.yml
          name: app-conf
          subPath: commons_hosts.yml

The deployment is successful but looking at the metacontroller logs and the controller logs, it looks like its looping when reconciling. The logs look like this:

I0822 07:44:15.443531       1 manage_children.go:210] reflect diff: a=observed, b=desired:

object[spec][template][spec][containers][0][volumeMounts][0][mountPath]:
  a: ...r/www/vhosts/XXXX/XXXX/adminwebservice/config/parameters.yml"
  b: ...r/www/vhosts/XXXX/XXXX/commons/config/hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][0][subPath]:
  a: "adminwebservice_parameters.yml"
  b: "commons_hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][1][mountPath]:
  a: "/var/www/vhosts/XXXX/XXXX/webservice/config/parameters.yml"
  b: "/var/www/vhosts/XXXX/XXXX/commons/config/hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][1][subPath]:
  a: "webservice_parameters.yml"
  b: "commons_hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][2][mountPath]:
  a: "/var/www/vhosts/XXXX/XXXX/frontend/config/parameters.yml"
  b: "/var/www/vhosts/XXXX/XXXX/commons/config/hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][2][subPath]:
  a: "frontend_parameters.yml"
  b: "commons_hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][3][mountPath]:
  a: "/var/www/vhosts/XXXX/XXXX/commons/config/parameters.yml"
  b: "/var/www/vhosts/XXXX/XXXX/commons/config/hosts.yml"
object[spec][template][spec][containers][0][volumeMounts][3][subPath]:
  a: "commons_parameters.yml"
  b: "commons_hosts.yml"

And subequently erroring because it is sending duplicate entries to the Deployment (as from above, all of the values seem to be the same in the desired section) .

Looking through the code, it looks like in dynamic_apply.mergeArray, it is trying to see if there are common keys in an array and merging the objects using the detectedKey. Since 'name' is one of the 'knownMergeKeys', it seems to be using that as a key and merging the different values wrongly? I havent exactly narrowed down where this is happening, but other configmaps mounted as volumes dont have this problem since they have different names, its only for the case where I have shared names but different mount paths.

I can get around this now by having different ConfigMaps for each of the files, but guess this is a bug.

rajasaur commented 5 years ago

Debugged a bit further, it looks like the issue is in makeListMap method, specifically at https://github.com/GoogleCloudPlatform/metacontroller/blob/master/dynamic/apply/apply.go#L186. It looks like if there are multiple volumemounts with the 'name' being common, the array gets overwritten with the last value as the key remains the same.

I am not able to identify the purpose of that method.. Is it to get the mergeKey and then create a List of Maps , keyed by the common mergeKey ?

rajasaur commented 5 years ago

I was also able to get past this by removing "name" from knownMergeKeys