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,14 @@ 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+ @ SuppressFBWarnings (value = "MS_SHOULD_BE_FINAL" , justification = "tests & emergency admin" )
78+ public static /* almost final*/ boolean DISABLE_OCTAL_MODES =
79+ Boolean .getBoolean (PodTemplateUtils .class .getName () + ".DISABLE_OCTAL_MODES" );
80+
7581 /**
7682 * Combines a {@link ContainerTemplate} with its parent.
7783 * @param parent The parent container template (nullable).
@@ -705,24 +711,42 @@ public static Pod parseFromYaml(String yaml) {
705711 if (podFromYaml .getSpec () == null ) {
706712 podFromYaml .setSpec (new PodSpec ());
707713 }
708- fixOctal (podFromYaml );
714+ if (!DISABLE_OCTAL_MODES ) {
715+ fixOctal (podFromYaml );
716+ }
709717 return podFromYaml ;
710718 }
711719 }
712720
713721 private static void fixOctal (@ NonNull Pod podFromYaml ) {
722+ podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getConfigMap ).forEach (configMap -> {
723+ if (configMap != null ) {
724+ var defaultMode = configMap .getDefaultMode ();
725+ if (defaultMode != null ) {
726+ configMap .setDefaultMode (convertPermissionToOctal (defaultMode ));
727+ }
728+ }
729+ });
730+ podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getSecret ).forEach (secretVolumeSource -> {
731+ if (secretVolumeSource != null ) {
732+ var defaultMode = secretVolumeSource .getDefaultMode ();
733+ if (defaultMode != null ) {
734+ secretVolumeSource .setDefaultMode (convertPermissionToOctal (defaultMode ));
735+ }
736+ }
737+ });
714738 podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getProjected ).forEach (projected -> {
715739 if (projected != null ) {
716- Integer defaultMode = projected .getDefaultMode ();
740+ var defaultMode = projected .getDefaultMode ();
717741 if (defaultMode != null ) {
718- projected .setDefaultMode (convertToOctal (defaultMode ));
742+ projected .setDefaultMode (convertPermissionToOctal (defaultMode ));
719743 }
720- projected .getSources ().stream (). forEach (source -> {
721- ConfigMapProjection configMap = source .getConfigMap ();
744+ projected .getSources ().forEach (source -> {
745+ var configMap = source .getConfigMap ();
722746 if (configMap != null ) {
723747 convertDecimalIntegersToOctal (configMap .getItems ());
724748 }
725- SecretProjection secret = source .getSecret ();
749+ var secret = source .getSecret ();
726750 if (secret != null ) {
727751 convertDecimalIntegersToOctal (secret .getItems ());
728752 }
@@ -732,16 +756,37 @@ private static void fixOctal(@NonNull Pod podFromYaml) {
732756 }
733757
734758 private static void convertDecimalIntegersToOctal (List <KeyToPath > items ) {
735- items .stream (). forEach (i -> {
736- Integer mode = i .getMode ();
759+ items .forEach (i -> {
760+ var mode = i .getMode ();
737761 if (mode != null ) {
738- i .setMode (convertToOctal (mode ));
762+ i .setMode (convertPermissionToOctal (mode ));
739763 }
740764 });
741765 }
742766
743- private static int convertToOctal (Integer defaultMode ) {
744- return Integer .parseInt (Integer .toString (defaultMode , 10 ), 8 );
767+ /**
768+ * Permissions are generally expressed in octal notation, e.g. 0777.
769+ * After parsing, this is stored as the integer 777, but the snakeyaml-engine does not convert to decimal first.
770+ * When the client later sends the pod spec to the server, it sends the integer as is through the json schema,
771+ * however the server expects a decimal, which means an integer between 0 and 511.
772+ *
773+ * The user can also provide permissions as a decimal integer, e.g. 511.
774+ *
775+ * This method attempts to guess whether the user provided a decimal or octal integer, and converts to octal if needed,
776+ * so that the resulting can be submitted to the server.
777+ *
778+ */
779+ static int convertPermissionToOctal (Integer i ) {
780+ // Permissions are expressed as octal integers
781+ // octal goes from 0000 to 0777
782+ // decimal goes from 0 to 511
783+ var s = Integer .toString (i , 10 );
784+ // If the input has a digit which is 8 or 9, this was likely a decimal input. Best effort support here.
785+ if (s .chars ().map (c -> c - '0' ).anyMatch (a -> a > 7 )) {
786+ return i ;
787+ } else {
788+ return Integer .parseInt (s , 8 );
789+ }
745790 }
746791
747792 public static Collection <String > validateYamlContainerNames (List <String > yamls ) {
0 commit comments