@Test public void testInvalidImagesFail() throws Exception { final Job.Builder b = Job.newBuilder().setName("foo").setVersion("1"); assertEquals(newHashSet("Tag cannot be empty"), validator.validate(b.setImage("repo:").build())); assertEquals(newHashSet("Tag cannot be empty"), validator.validate(b.setImage("repo:").build())); assertEquals(newHashSet("Tag cannot be empty"), validator.validate(b.setImage("repo:" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Digest cannot be empty"), validator.validate(b.setImage("foo@").build())); assertEquals(newHashSet("Illegal digest: \":123\""), validator.validate(b.setImage("foo@:123").build())); assertEquals(newHashSet("Illegal digest: \":123\""), validator.validate(b.setImage("foo:bar@:123").build())); assertEquals(newHashSet("Illegal digest: \"sha256:\""), validator.validate(b.setImage("foo@sha256:").build())); assertEquals(newHashSet("Illegal digest: \"sha256:\""), validator.validate(b.setImage("foo:bar@sha256:").build())); assertEquals(newHashSet("Illegal digest: \"sha256:\""), validator.validate(b.setImage("foo:4711/baz:bar@sha256:").build())); assertFalse(validator.validate(b.setImage("repo:/").build()).isEmpty()); assertEquals(newHashSet("Invalid domain name: \"1.2.3.4.\""), validator.validate(b.setImage("1.2.3.4.:4711/namespace/repo").build())); assertEquals(newHashSet("Invalid domain name: \" reg.istry\""), validator.validate(b.setImage(" reg.istry:4711/repo").build())); assertEquals(newHashSet("Invalid domain name: \"reg .istry\""), validator.validate(b.setImage("reg .istry:4711/repo").build())); assertEquals(newHashSet("Invalid domain name: \"reg.istry \""), validator.validate(b.setImage("reg.istry :4711/repo").build())); assertEquals(newHashSet("Invalid domain name: \"1.2.3.4.\""), validator.validate(b.setImage("1.2.3.4.:4711/namespace/repo" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Invalid port in endpoint: \"reg.istry: 4711\""), validator.validate(b.setImage("reg.istry: 4711/repo").build())); assertEquals(newHashSet("Invalid port in endpoint: \"reg.istry:4711 \""), validator.validate(b.setImage("reg.istry:4711 /repo").build())); assertEquals(newHashSet("Invalid port in endpoint: \"reg.istry:4711 \""), validator.validate(b.setImage("reg.istry:4711 /repo" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Invalid image name (reg.istry:4711/ repo), only ^([a-z0-9._-]+)$ is " + "allowed for each slash-separated name component " + "(failed on \" repo\")"), validator.validate(b.setImage("reg.istry:4711/ repo").build())); assertEquals(newHashSet("Invalid image name (reg.istry:4711/namespace /repo), only " + "^([a-z0-9._-]+)$ is allowed for each slash-separated name component " + "(failed on \"namespace \")"), validator.validate(b.setImage("reg.istry:4711/namespace /repo").build())); assertEquals(newHashSet("Invalid image name (reg.istry:4711/namespace/ repo), only " + "^([a-z0-9._-]+)$ is allowed for each slash-separated name component " + "(failed on \" repo\")"), validator.validate(b.setImage("reg.istry:4711/namespace/ repo").build())); assertEquals(newHashSet("Invalid image name (reg.istry:4711/namespace/repo ), only " + "^([a-z0-9._-]+)$ is allowed for each slash-separated name component " + "(failed on \"repo \")"), validator.validate(b.setImage("reg.istry:4711/namespace/repo ").build())); assertEquals(newHashSet("Invalid image name (reg.istry:4711/namespace/ repo" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae), only " + "^([a-z0-9._-]+)$ is allowed for each slash-separated name component " + "(failed on \" repo\")"), validator.validate(b.setImage("reg.istry:4711/namespace/ repo" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Invalid domain name: \"foo-.ba|z\""), validator.validate(b.setImage("foo-.ba|z/namespace/baz").build())); assertEquals(newHashSet("Invalid domain name: \"reg..istry\""), validator.validate(b.setImage("reg..istry/namespace/baz").build())); assertEquals(newHashSet("Invalid domain name: \"reg..istry\""), validator.validate(b.setImage("reg..istry/namespace/baz").build())); assertEquals(newHashSet("Invalid domain name: \"reg..istry\""), validator.validate(b.setImage("reg..istry/namespace/baz:foo").build())); assertEquals(newHashSet("Invalid domain name: \"reg..istry\""), validator.validate(b.setImage("reg..istry/namespace/baz:foo" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Invalid domain name: \"reg..istry\""), validator.validate(b.setImage("reg..istry/namespace/baz" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Invalid port in endpoint: \"foo:345345345\""), validator.validate(b.setImage("foo:345345345/namespace/baz").build())); assertEquals(newHashSet("Invalid port in endpoint: \"foo:345345345\""), validator.validate(b.setImage("foo:345345345/namespace/baz:bar").build())); assertEquals(newHashSet("Invalid port in endpoint: \"foo:345345345\""), validator.validate(b.setImage("foo:345345345/namespace/baz:bar" + "@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae").build())); assertEquals(newHashSet("Invalid port in endpoint: \"foo:-17\""), validator.validate(b.setImage("foo:-17/namespace/baz").build())); final String foos = Strings.repeat("foo", 100); final String image = foos + "/bar"; assertEquals(newHashSet("Invalid image name (" + image + "), repository name cannot be larger" + " than 255 characters"), validator.validate(b.setImage(image).build())); }
public Set<String> validate(final Job job) { final Set<String> errors = Sets.newHashSet(); errors.addAll(validateJobId(job)); errors.addAll(validateJobImage(job.getImage())); errors.addAll(validateJobHostName(job.getHostname())); final Set<String> externalPortsWithProto = Sets.newHashSet(); for (final PortMapping mapping : job.getPorts().values()) { final Integer externalMappedPort = mapping.getExternalPort(); String externalMappedPortWithProto = String.format( "%s:%d", mapping.getProtocol(), externalMappedPort != null ? externalMappedPort : 0); if (externalPortsWithProto.contains(externalMappedPortWithProto) && externalMappedPort != null) { errors.add(format("Duplicate external port mapping: %s", externalMappedPortWithProto)); } externalPortsWithProto.add(externalMappedPortWithProto); } for (final Map.Entry<String, PortMapping> entry : job.getPorts().entrySet()) { final String name = entry.getKey(); final PortMapping mapping = entry.getValue(); if (!PORT_MAPPING_PROTO_PATTERN.matcher(mapping.getProtocol()).matches()) { errors.add(format("Invalid port mapping protocol: %s", mapping.getProtocol())); } if (!legalPort(mapping.getInternalPort())) { errors.add(format("Invalid internal port: %d", mapping.getInternalPort())); } if (mapping.getExternalPort() != null && !legalPort(mapping.getExternalPort())) { errors.add(format("Invalid external port: %d", mapping.getExternalPort())); } if (!PORT_MAPPING_NAME_PATTERN.matcher(name).matches()) { errors.add(format("Invalid port mapping endpoint name: %s", name)); } } for (final ServiceEndpoint registration : job.getRegistration().keySet()) { final ServicePorts servicePorts = job.getRegistration().get(registration); if (servicePorts == null || servicePorts.getPorts() == null) { errors.add(format("registration for '%s' is malformed: does not have a port mapping", registration.getName())); continue; } for (final String portName : servicePorts.getPorts().keySet()) { if (!job.getPorts().containsKey(portName)) { errors.add(format("Service registration refers to missing port mapping: %s=%s", registration, portName)); } if (!REGISTRATION_NAME_PATTERN.matcher(registration.getName()).matches()) { errors.add(format("Invalid service registration name: %s", registration.getName())); } } } for (final Map.Entry<String, String> entry : job.getVolumes().entrySet()) { final String path = entry.getKey(); final String source = entry.getValue(); if (!path.startsWith("/")) { errors.add("Volume path is not absolute: " + path); continue; } if (source.contains("/") && !source.startsWith("/")) { errors.add("Volume source is not absolute: " + source); continue; } final String[] parts = path.split(":", 3); if (path.isEmpty() || path.equals("/") || parts.length > 2 || (parts.length > 1 && parts[1].isEmpty())) { errors.add(format("Invalid volume path: %s", path)); } } final Date expiry = job.getExpires(); final Date now = new Date(); if (expiry != null && expiry.before(now)) { errors.add("Job expires in the past - " + expiry + " is before " + now); } errors.addAll(validateJobHealthCheck(job)); errors.addAll(validateJobNetworkMode(job)); if (shouldValidateAddCapabilities) { errors.addAll(validateAddCapabilities(job)); } for (final String mountPoint : job.getRamdisks().keySet()) { if (!mountPoint.startsWith("/")) { errors.add("Ramdisk mount point is not absolute: " + mountPoint); continue; } } final Set<String> volumeMountPoints = Sets.newHashSet(); for (String s : job.getVolumes().keySet()) { volumeMountPoints.add(s.split(":", 2)[0]); } for (final String mountPoint : job.getRamdisks().keySet()) { if (volumeMountPoints.contains(mountPoint)) { errors.add(format("Ramdisk mount point used by volume: %s", mountPoint)); } } errors.addAll(validateGracePeriodAndExternalPorts(job)); return errors; }
JobValidator { public Set<String> validate(final Job job) { final Set<String> errors = Sets.newHashSet(); errors.addAll(validateJobId(job)); errors.addAll(validateJobImage(job.getImage())); errors.addAll(validateJobHostName(job.getHostname())); final Set<String> externalPortsWithProto = Sets.newHashSet(); for (final PortMapping mapping : job.getPorts().values()) { final Integer externalMappedPort = mapping.getExternalPort(); String externalMappedPortWithProto = String.format( "%s:%d", mapping.getProtocol(), externalMappedPort != null ? externalMappedPort : 0); if (externalPortsWithProto.contains(externalMappedPortWithProto) && externalMappedPort != null) { errors.add(format("Duplicate external port mapping: %s", externalMappedPortWithProto)); } externalPortsWithProto.add(externalMappedPortWithProto); } for (final Map.Entry<String, PortMapping> entry : job.getPorts().entrySet()) { final String name = entry.getKey(); final PortMapping mapping = entry.getValue(); if (!PORT_MAPPING_PROTO_PATTERN.matcher(mapping.getProtocol()).matches()) { errors.add(format("Invalid port mapping protocol: %s", mapping.getProtocol())); } if (!legalPort(mapping.getInternalPort())) { errors.add(format("Invalid internal port: %d", mapping.getInternalPort())); } if (mapping.getExternalPort() != null && !legalPort(mapping.getExternalPort())) { errors.add(format("Invalid external port: %d", mapping.getExternalPort())); } if (!PORT_MAPPING_NAME_PATTERN.matcher(name).matches()) { errors.add(format("Invalid port mapping endpoint name: %s", name)); } } for (final ServiceEndpoint registration : job.getRegistration().keySet()) { final ServicePorts servicePorts = job.getRegistration().get(registration); if (servicePorts == null || servicePorts.getPorts() == null) { errors.add(format("registration for '%s' is malformed: does not have a port mapping", registration.getName())); continue; } for (final String portName : servicePorts.getPorts().keySet()) { if (!job.getPorts().containsKey(portName)) { errors.add(format("Service registration refers to missing port mapping: %s=%s", registration, portName)); } if (!REGISTRATION_NAME_PATTERN.matcher(registration.getName()).matches()) { errors.add(format("Invalid service registration name: %s", registration.getName())); } } } for (final Map.Entry<String, String> entry : job.getVolumes().entrySet()) { final String path = entry.getKey(); final String source = entry.getValue(); if (!path.startsWith("/")) { errors.add("Volume path is not absolute: " + path); continue; } if (source.contains("/") && !source.startsWith("/")) { errors.add("Volume source is not absolute: " + source); continue; } final String[] parts = path.split(":", 3); if (path.isEmpty() || path.equals("/") || parts.length > 2 || (parts.length > 1 && parts[1].isEmpty())) { errors.add(format("Invalid volume path: %s", path)); } } final Date expiry = job.getExpires(); final Date now = new Date(); if (expiry != null && expiry.before(now)) { errors.add("Job expires in the past - " + expiry + " is before " + now); } errors.addAll(validateJobHealthCheck(job)); errors.addAll(validateJobNetworkMode(job)); if (shouldValidateAddCapabilities) { errors.addAll(validateAddCapabilities(job)); } for (final String mountPoint : job.getRamdisks().keySet()) { if (!mountPoint.startsWith("/")) { errors.add("Ramdisk mount point is not absolute: " + mountPoint); continue; } } final Set<String> volumeMountPoints = Sets.newHashSet(); for (String s : job.getVolumes().keySet()) { volumeMountPoints.add(s.split(":", 2)[0]); } for (final String mountPoint : job.getRamdisks().keySet()) { if (volumeMountPoints.contains(mountPoint)) { errors.add(format("Ramdisk mount point used by volume: %s", mountPoint)); } } errors.addAll(validateGracePeriodAndExternalPorts(job)); return errors; } }
JobValidator { public Set<String> validate(final Job job) { final Set<String> errors = Sets.newHashSet(); errors.addAll(validateJobId(job)); errors.addAll(validateJobImage(job.getImage())); errors.addAll(validateJobHostName(job.getHostname())); final Set<String> externalPortsWithProto = Sets.newHashSet(); for (final PortMapping mapping : job.getPorts().values()) { final Integer externalMappedPort = mapping.getExternalPort(); String externalMappedPortWithProto = String.format( "%s:%d", mapping.getProtocol(), externalMappedPort != null ? externalMappedPort : 0); if (externalPortsWithProto.contains(externalMappedPortWithProto) && externalMappedPort != null) { errors.add(format("Duplicate external port mapping: %s", externalMappedPortWithProto)); } externalPortsWithProto.add(externalMappedPortWithProto); } for (final Map.Entry<String, PortMapping> entry : job.getPorts().entrySet()) { final String name = entry.getKey(); final PortMapping mapping = entry.getValue(); if (!PORT_MAPPING_PROTO_PATTERN.matcher(mapping.getProtocol()).matches()) { errors.add(format("Invalid port mapping protocol: %s", mapping.getProtocol())); } if (!legalPort(mapping.getInternalPort())) { errors.add(format("Invalid internal port: %d", mapping.getInternalPort())); } if (mapping.getExternalPort() != null && !legalPort(mapping.getExternalPort())) { errors.add(format("Invalid external port: %d", mapping.getExternalPort())); } if (!PORT_MAPPING_NAME_PATTERN.matcher(name).matches()) { errors.add(format("Invalid port mapping endpoint name: %s", name)); } } for (final ServiceEndpoint registration : job.getRegistration().keySet()) { final ServicePorts servicePorts = job.getRegistration().get(registration); if (servicePorts == null || servicePorts.getPorts() == null) { errors.add(format("registration for '%s' is malformed: does not have a port mapping", registration.getName())); continue; } for (final String portName : servicePorts.getPorts().keySet()) { if (!job.getPorts().containsKey(portName)) { errors.add(format("Service registration refers to missing port mapping: %s=%s", registration, portName)); } if (!REGISTRATION_NAME_PATTERN.matcher(registration.getName()).matches()) { errors.add(format("Invalid service registration name: %s", registration.getName())); } } } for (final Map.Entry<String, String> entry : job.getVolumes().entrySet()) { final String path = entry.getKey(); final String source = entry.getValue(); if (!path.startsWith("/")) { errors.add("Volume path is not absolute: " + path); continue; } if (source.contains("/") && !source.startsWith("/")) { errors.add("Volume source is not absolute: " + source); continue; } final String[] parts = path.split(":", 3); if (path.isEmpty() || path.equals("/") || parts.length > 2 || (parts.length > 1 && parts[1].isEmpty())) { errors.add(format("Invalid volume path: %s", path)); } } final Date expiry = job.getExpires(); final Date now = new Date(); if (expiry != null && expiry.before(now)) { errors.add("Job expires in the past - " + expiry + " is before " + now); } errors.addAll(validateJobHealthCheck(job)); errors.addAll(validateJobNetworkMode(job)); if (shouldValidateAddCapabilities) { errors.addAll(validateAddCapabilities(job)); } for (final String mountPoint : job.getRamdisks().keySet()) { if (!mountPoint.startsWith("/")) { errors.add("Ramdisk mount point is not absolute: " + mountPoint); continue; } } final Set<String> volumeMountPoints = Sets.newHashSet(); for (String s : job.getVolumes().keySet()) { volumeMountPoints.add(s.split(":", 2)[0]); } for (final String mountPoint : job.getRamdisks().keySet()) { if (volumeMountPoints.contains(mountPoint)) { errors.add(format("Ramdisk mount point used by volume: %s", mountPoint)); } } errors.addAll(validateGracePeriodAndExternalPorts(job)); return errors; } JobValidator();  JobValidator(final boolean shouldValidateJobHash);  JobValidator(final boolean shouldValidateJobHash,
                      final boolean shouldValidateAddCapabilities);  JobValidator(final boolean shouldValidateJobHash,
                      final boolean shouldValidateAddCapabilities,
                      final Set<String> whitelistedCapabilities); }
JobValidator { public Set<String> validate(final Job job) { final Set<String> errors = Sets.newHashSet(); errors.addAll(validateJobId(job)); errors.addAll(validateJobImage(job.getImage())); errors.addAll(validateJobHostName(job.getHostname())); final Set<String> externalPortsWithProto = Sets.newHashSet(); for (final PortMapping mapping : job.getPorts().values()) { final Integer externalMappedPort = mapping.getExternalPort(); String externalMappedPortWithProto = String.format( "%s:%d", mapping.getProtocol(), externalMappedPort != null ? externalMappedPort : 0); if (externalPortsWithProto.contains(externalMappedPortWithProto) && externalMappedPort != null) { errors.add(format("Duplicate external port mapping: %s", externalMappedPortWithProto)); } externalPortsWithProto.add(externalMappedPortWithProto); } for (final Map.Entry<String, PortMapping> entry : job.getPorts().entrySet()) { final String name = entry.getKey(); final PortMapping mapping = entry.getValue(); if (!PORT_MAPPING_PROTO_PATTERN.matcher(mapping.getProtocol()).matches()) { errors.add(format("Invalid port mapping protocol: %s", mapping.getProtocol())); } if (!legalPort(mapping.getInternalPort())) { errors.add(format("Invalid internal port: %d", mapping.getInternalPort())); } if (mapping.getExternalPort() != null && !legalPort(mapping.getExternalPort())) { errors.add(format("Invalid external port: %d", mapping.getExternalPort())); } if (!PORT_MAPPING_NAME_PATTERN.matcher(name).matches()) { errors.add(format("Invalid port mapping endpoint name: %s", name)); } } for (final ServiceEndpoint registration : job.getRegistration().keySet()) { final ServicePorts servicePorts = job.getRegistration().get(registration); if (servicePorts == null || servicePorts.getPorts() == null) { errors.add(format("registration for '%s' is malformed: does not have a port mapping", registration.getName())); continue; } for (final String portName : servicePorts.getPorts().keySet()) { if (!job.getPorts().containsKey(portName)) { errors.add(format("Service registration refers to missing port mapping: %s=%s", registration, portName)); } if (!REGISTRATION_NAME_PATTERN.matcher(registration.getName()).matches()) { errors.add(format("Invalid service registration name: %s", registration.getName())); } } } for (final Map.Entry<String, String> entry : job.getVolumes().entrySet()) { final String path = entry.getKey(); final String source = entry.getValue(); if (!path.startsWith("/")) { errors.add("Volume path is not absolute: " + path); continue; } if (source.contains("/") && !source.startsWith("/")) { errors.add("Volume source is not absolute: " + source); continue; } final String[] parts = path.split(":", 3); if (path.isEmpty() || path.equals("/") || parts.length > 2 || (parts.length > 1 && parts[1].isEmpty())) { errors.add(format("Invalid volume path: %s", path)); } } final Date expiry = job.getExpires(); final Date now = new Date(); if (expiry != null && expiry.before(now)) { errors.add("Job expires in the past - " + expiry + " is before " + now); } errors.addAll(validateJobHealthCheck(job)); errors.addAll(validateJobNetworkMode(job)); if (shouldValidateAddCapabilities) { errors.addAll(validateAddCapabilities(job)); } for (final String mountPoint : job.getRamdisks().keySet()) { if (!mountPoint.startsWith("/")) { errors.add("Ramdisk mount point is not absolute: " + mountPoint); continue; } } final Set<String> volumeMountPoints = Sets.newHashSet(); for (String s : job.getVolumes().keySet()) { volumeMountPoints.add(s.split(":", 2)[0]); } for (final String mountPoint : job.getRamdisks().keySet()) { if (volumeMountPoints.contains(mountPoint)) { errors.add(format("Ramdisk mount point used by volume: %s", mountPoint)); } } errors.addAll(validateGracePeriodAndExternalPorts(job)); return errors; } JobValidator();  JobValidator(final boolean shouldValidateJobHash);  JobValidator(final boolean shouldValidateJobHash,
                      final boolean shouldValidateAddCapabilities);  JobValidator(final boolean shouldValidateJobHash,
                      final boolean shouldValidateAddCapabilities,
                      final Set<String> whitelistedCapabilities); Set<String> validate(final Job job); }
JobValidator { public Set<String> validate(final Job job) { final Set<String> errors = Sets.newHashSet(); errors.addAll(validateJobId(job)); errors.addAll(validateJobImage(job.getImage())); errors.addAll(validateJobHostName(job.getHostname())); final Set<String> externalPortsWithProto = Sets.newHashSet(); for (final PortMapping mapping : job.getPorts().values()) { final Integer externalMappedPort = mapping.getExternalPort(); String externalMappedPortWithProto = String.format( "%s:%d", mapping.getProtocol(), externalMappedPort != null ? externalMappedPort : 0); if (externalPortsWithProto.contains(externalMappedPortWithProto) && externalMappedPort != null) { errors.add(format("Duplicate external port mapping: %s", externalMappedPortWithProto)); } externalPortsWithProto.add(externalMappedPortWithProto); } for (final Map.Entry<String, PortMapping> entry : job.getPorts().entrySet()) { final String name = entry.getKey(); final PortMapping mapping = entry.getValue(); if (!PORT_MAPPING_PROTO_PATTERN.matcher(mapping.getProtocol()).matches()) { errors.add(format("Invalid port mapping protocol: %s", mapping.getProtocol())); } if (!legalPort(mapping.getInternalPort())) { errors.add(format("Invalid internal port: %d", mapping.getInternalPort())); } if (mapping.getExternalPort() != null && !legalPort(mapping.getExternalPort())) { errors.add(format("Invalid external port: %d", mapping.getExternalPort())); } if (!PORT_MAPPING_NAME_PATTERN.matcher(name).matches()) { errors.add(format("Invalid port mapping endpoint name: %s", name)); } } for (final ServiceEndpoint registration : job.getRegistration().keySet()) { final ServicePorts servicePorts = job.getRegistration().get(registration); if (servicePorts == null || servicePorts.getPorts() == null) { errors.add(format("registration for '%s' is malformed: does not have a port mapping", registration.getName())); continue; } for (final String portName : servicePorts.getPorts().keySet()) { if (!job.getPorts().containsKey(portName)) { errors.add(format("Service registration refers to missing port mapping: %s=%s", registration, portName)); } if (!REGISTRATION_NAME_PATTERN.matcher(registration.getName()).matches()) { errors.add(format("Invalid service registration name: %s", registration.getName())); } } } for (final Map.Entry<String, String> entry : job.getVolumes().entrySet()) { final String path = entry.getKey(); final String source = entry.getValue(); if (!path.startsWith("/")) { errors.add("Volume path is not absolute: " + path); continue; } if (source.contains("/") && !source.startsWith("/")) { errors.add("Volume source is not absolute: " + source); continue; } final String[] parts = path.split(":", 3); if (path.isEmpty() || path.equals("/") || parts.length > 2 || (parts.length > 1 && parts[1].isEmpty())) { errors.add(format("Invalid volume path: %s", path)); } } final Date expiry = job.getExpires(); final Date now = new Date(); if (expiry != null && expiry.before(now)) { errors.add("Job expires in the past - " + expiry + " is before " + now); } errors.addAll(validateJobHealthCheck(job)); errors.addAll(validateJobNetworkMode(job)); if (shouldValidateAddCapabilities) { errors.addAll(validateAddCapabilities(job)); } for (final String mountPoint : job.getRamdisks().keySet()) { if (!mountPoint.startsWith("/")) { errors.add("Ramdisk mount point is not absolute: " + mountPoint); continue; } } final Set<String> volumeMountPoints = Sets.newHashSet(); for (String s : job.getVolumes().keySet()) { volumeMountPoints.add(s.split(":", 2)[0]); } for (final String mountPoint : job.getRamdisks().keySet()) { if (volumeMountPoints.contains(mountPoint)) { errors.add(format("Ramdisk mount point used by volume: %s", mountPoint)); } } errors.addAll(validateGracePeriodAndExternalPorts(job)); return errors; } JobValidator();  JobValidator(final boolean shouldValidateJobHash);  JobValidator(final boolean shouldValidateJobHash,
                      final boolean shouldValidateAddCapabilities);  JobValidator(final boolean shouldValidateJobHash,
                      final boolean shouldValidateAddCapabilities,
                      final Set<String> whitelistedCapabilities); Set<String> validate(final Job job);  }