14
14
import hudson .model .Label ;
15
15
import hudson .model .Node ;
16
16
import hudson .slaves .NodeProperty ;
17
- import io .fabric8 .kubernetes .api .model .ConfigMapProjection ;
18
17
import io .fabric8 .kubernetes .api .model .Container ;
19
18
import io .fabric8 .kubernetes .api .model .ContainerBuilder ;
20
19
import io .fabric8 .kubernetes .api .model .EnvFromSource ;
27
26
import io .fabric8 .kubernetes .api .model .PodSpec ;
28
27
import io .fabric8 .kubernetes .api .model .Quantity ;
29
28
import io .fabric8 .kubernetes .api .model .ResourceRequirements ;
30
- import io .fabric8 .kubernetes .api .model .SecretProjection ;
31
29
import io .fabric8 .kubernetes .api .model .Toleration ;
32
30
import io .fabric8 .kubernetes .api .model .Volume ;
33
31
import io .fabric8 .kubernetes .api .model .VolumeMount ;
@@ -72,6 +70,14 @@ public class PodTemplateUtils {
72
70
@ SuppressFBWarnings (value = "MS_SHOULD_BE_FINAL" , justification = "tests & emergency admin" )
73
71
public static boolean SUBSTITUTE_ENV = Boolean .getBoolean (PodTemplateUtils .class .getName () + ".SUBSTITUTE_ENV" );
74
72
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
+
75
81
/**
76
82
* Combines a {@link ContainerTemplate} with its parent.
77
83
* @param parent The parent container template (nullable).
@@ -705,24 +711,42 @@ public static Pod parseFromYaml(String yaml) {
705
711
if (podFromYaml .getSpec () == null ) {
706
712
podFromYaml .setSpec (new PodSpec ());
707
713
}
708
- fixOctal (podFromYaml );
714
+ if (!DISABLE_OCTAL_MODES ) {
715
+ fixOctal (podFromYaml );
716
+ }
709
717
return podFromYaml ;
710
718
}
711
719
}
712
720
713
721
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
+ });
714
738
podFromYaml .getSpec ().getVolumes ().stream ().map (Volume ::getProjected ).forEach (projected -> {
715
739
if (projected != null ) {
716
- Integer defaultMode = projected .getDefaultMode ();
740
+ var defaultMode = projected .getDefaultMode ();
717
741
if (defaultMode != null ) {
718
- projected .setDefaultMode (convertToOctal (defaultMode ));
742
+ projected .setDefaultMode (convertPermissionToOctal (defaultMode ));
719
743
}
720
- projected .getSources ().stream (). forEach (source -> {
721
- ConfigMapProjection configMap = source .getConfigMap ();
744
+ projected .getSources ().forEach (source -> {
745
+ var configMap = source .getConfigMap ();
722
746
if (configMap != null ) {
723
747
convertDecimalIntegersToOctal (configMap .getItems ());
724
748
}
725
- SecretProjection secret = source .getSecret ();
749
+ var secret = source .getSecret ();
726
750
if (secret != null ) {
727
751
convertDecimalIntegersToOctal (secret .getItems ());
728
752
}
@@ -732,16 +756,37 @@ private static void fixOctal(@NonNull Pod podFromYaml) {
732
756
}
733
757
734
758
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 ();
737
761
if (mode != null ) {
738
- i .setMode (convertToOctal (mode ));
762
+ i .setMode (convertPermissionToOctal (mode ));
739
763
}
740
764
});
741
765
}
742
766
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
+ }
745
790
}
746
791
747
792
public static Collection <String > validateYamlContainerNames (List <String > yamls ) {
0 commit comments