1414import hudson .model .Label ;
1515import hudson .model .Node ;
1616import hudson .slaves .NodeProperty ;
17- import io .fabric8 .kubernetes .api .model .ConfigMapProjection ;
1817import io .fabric8 .kubernetes .api .model .Container ;
1918import io .fabric8 .kubernetes .api .model .ContainerBuilder ;
2019import io .fabric8 .kubernetes .api .model .EnvFromSource ;
2726import io .fabric8 .kubernetes .api .model .PodSpec ;
2827import io .fabric8 .kubernetes .api .model .Quantity ;
2928import io .fabric8 .kubernetes .api .model .ResourceRequirements ;
30- import io .fabric8 .kubernetes .api .model .SecretProjection ;
3129import io .fabric8 .kubernetes .api .model .Toleration ;
3230import io .fabric8 .kubernetes .api .model .Volume ;
3331import io .fabric8 .kubernetes .api .model .VolumeMount ;
@@ -72,6 +70,13 @@ public class PodTemplateUtils {
7270 @ SuppressFBWarnings (value = "MS_SHOULD_BE_FINAL" , justification = "tests & emergency admin" )
7371 public static boolean SUBSTITUTE_ENV = Boolean .getBoolean (PodTemplateUtils .class .getName () + ".SUBSTITUTE_ENV" );
7472
73+ /**
74+ * If true, all modes permissions provided to pods are expected to be provided in decimal notation.
75+ * Otherwise, the plugin will consider they are written in octal notation.
76+ */
77+ public static /* almost final*/ boolean DISABLE_OCTAL_MODES =
78+ Boolean .getBoolean (PodTemplateUtils .class .getName () + ".DISABLE_OCTAL_MODES" );
79+
7580 /**
7681 * Combines a {@link ContainerTemplate} with its parent.
7782 * @param parent The parent container template (nullable).
@@ -705,24 +710,42 @@ public static Pod parseFromYaml(String yaml) {
705710 if (podFromYaml .getSpec () == null ) {
706711 podFromYaml .setSpec (new PodSpec ());
707712 }
708- fixOctal (podFromYaml );
713+ if (!DISABLE_OCTAL_MODES ) {
714+ fixOctal (podFromYaml );
715+ }
709716 return podFromYaml ;
710717 }
711718 }
712719
713720 private static void fixOctal (@ NonNull Pod podFromYaml ) {
721+ podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getConfigMap ).forEach (configMap -> {
722+ if (configMap != null ) {
723+ var defaultMode = configMap .getDefaultMode ();
724+ if (defaultMode != null ) {
725+ configMap .setDefaultMode (convertPermissionToOctal (defaultMode ));
726+ }
727+ }
728+ });
729+ podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getSecret ).forEach (secretVolumeSource -> {
730+ if (secretVolumeSource != null ) {
731+ var defaultMode = secretVolumeSource .getDefaultMode ();
732+ if (defaultMode != null ) {
733+ secretVolumeSource .setDefaultMode (convertPermissionToOctal (defaultMode ));
734+ }
735+ }
736+ });
714737 podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getProjected ).forEach (projected -> {
715738 if (projected != null ) {
716- Integer defaultMode = projected .getDefaultMode ();
739+ var defaultMode = projected .getDefaultMode ();
717740 if (defaultMode != null ) {
718- projected .setDefaultMode (convertToOctal (defaultMode ));
741+ projected .setDefaultMode (convertPermissionToOctal (defaultMode ));
719742 }
720- projected .getSources ().stream (). forEach (source -> {
721- ConfigMapProjection configMap = source .getConfigMap ();
743+ projected .getSources ().forEach (source -> {
744+ var configMap = source .getConfigMap ();
722745 if (configMap != null ) {
723746 convertDecimalIntegersToOctal (configMap .getItems ());
724747 }
725- SecretProjection secret = source .getSecret ();
748+ var secret = source .getSecret ();
726749 if (secret != null ) {
727750 convertDecimalIntegersToOctal (secret .getItems ());
728751 }
@@ -732,16 +755,37 @@ private static void fixOctal(@NonNull Pod podFromYaml) {
732755 }
733756
734757 private static void convertDecimalIntegersToOctal (List <KeyToPath > items ) {
735- items .stream (). forEach (i -> {
736- Integer mode = i .getMode ();
758+ items .forEach (i -> {
759+ var mode = i .getMode ();
737760 if (mode != null ) {
738- i .setMode (convertToOctal (mode ));
761+ i .setMode (convertPermissionToOctal (mode ));
739762 }
740763 });
741764 }
742765
743- private static int convertToOctal (Integer defaultMode ) {
744- return Integer .parseInt (Integer .toString (defaultMode , 10 ), 8 );
766+ /**
767+ * Permissions are generally expressed in octal notation, e.g. 0777.
768+ * After parsing, this is stored as the integer 777, but the snakeyaml-engine does not convert to decimal first.
769+ * When the client later sends the pod spec to the server, it sends the integer as is through the json schema,
770+ * however the server expects a decimal, which means an integer between 0 and 511.
771+ *
772+ * The user can also provide permissions as a decimal integer, e.g. 511.
773+ *
774+ * This method attempts to guess whether the user provided a decimal or octal integer, and converts to octal if needed,
775+ * so that the resulting can be submitted to the server.
776+ *
777+ */
778+ static int convertPermissionToOctal (Integer i ) {
779+ // Permissions are expressed as octal integers
780+ // octal goes from 0000 to 0777
781+ // decimal goes from 0 to 511
782+ var s = Integer .toString (i , 10 );
783+ // If the input has a digit which is 8 or 9, this was likely a decimal input. Best effort support here.
784+ if (s .chars ().map (c -> c - '0' ).anyMatch (a -> a > 7 )) {
785+ return i ;
786+ } else {
787+ return Integer .parseInt (s , 8 );
788+ }
745789 }
746790
747791 public static Collection <String > validateYamlContainerNames (List <String > yamls ) {
0 commit comments