if (!file.isFile() || !file.canRead()) {
throw new IllegalArgumentException("Cannot read file " + file);
}
final byte[] bytes = Files.readAllBytes(file.toPath());
final String config = new String(bytes, UTF_8);
final Job job = Json.read(config, Job.class);
builder = job.toBuilder();
} else if (templateJobId != null) {
final Map<JobId, Job> jobs = client.jobs(templateJobId).get();
if (jobs.size() == 0) {
if (!json) {
out.printf("Unknown job: %s%n", templateJobId);
} else {
CreateJobResponse createJobResponse =
new CreateJobResponse(CreateJobResponse.Status.UNKNOWN_JOB, null, null);
out.printf(createJobResponse.toJsonString());
}
return 1;
} else if (jobs.size() > 1) {
if (!json) {
out.printf("Ambiguous job reference: %s%n", templateJobId);
} else {
CreateJobResponse createJobResponse =
new CreateJobResponse(CreateJobResponse.Status.AMBIGUOUS_JOB_REFERENCE, null, null);
out.printf(createJobResponse.toJsonString());
}
return 1;
}
final Job template = Iterables.getOnlyElement(jobs.values());
builder = template.toBuilder();
if (id == null) {
throw new IllegalArgumentException("Please specify new job name and version");
}
} else {
if (id == null || imageIdentifier == null) {
throw new IllegalArgumentException(
"Please specify a file, or a template, or a job name, version and container image");
}
builder = Job.newBuilder();
}
// Merge job configuration options from command line arguments
if (id != null) {
final String[] parts = id.split(":");
switch (parts.length) {
case 3:
builder.setHash(parts[2]);
// fall through
case 2:
builder.setVersion(parts[1]);
// fall through
case 1:
builder.setName(parts[0]);
break;
default:
throw new IllegalArgumentException("Invalid Job id: " + id);
}
}
if (imageIdentifier != null) {
builder.setImage(imageIdentifier);
}
final List<String> command = options.getList(argsArg.getDest());
if (command != null && !command.isEmpty()) {
builder.setCommand(command);
}
final List<String> envList = options.getList(envArg.getDest());
if (!envList.isEmpty()) {
final Map<String, String> env = Maps.newHashMap();
// Add environmental variables from helios job configuration file
env.putAll(builder.getEnv());
// Add environmental variables passed in via CLI
// Overwrite any redundant keys to make CLI args take precedence
for (final String s : envList) {
final String[] parts = s.split("=", 2);
if (parts.length != 2) {
throw new IllegalArgumentException("Bad environment variable: " + s);
}
env.put(parts[0], parts[1]);
}
builder.setEnv(env);
}
// Parse port mappings
final List<String> portSpecs = options.getList(portArg.getDest());
final Map<String, PortMapping> explicitPorts = Maps.newHashMap();
final Pattern portPattern = compile("(?<n>[_\\-\\w]+)=(?<i>\\d+)(:(?<e>\\d+))?(/(?<p>\\w+))?");
for (final String spec : portSpecs) {
final Matcher matcher = portPattern.matcher(spec);
if (!matcher.matches()) {
throw new IllegalArgumentException("Bad port mapping: " + spec);
}
final String portName = matcher.group("n");
final int internal = Integer.parseInt(matcher.group("i"));
final Integer external = nullOrInteger(matcher.group("e"));
final String protocol = fromNullable(matcher.group("p")).or(TCP);
if (explicitPorts.containsKey(portName)) {
throw new IllegalArgumentException("Duplicate port mapping: " + portName);
}
explicitPorts.put(portName, PortMapping.of(internal, external, protocol));
}
// Merge port mappings
final Map<String, PortMapping> ports = Maps.newHashMap();
ports.putAll(builder.getPorts());
ports.putAll(explicitPorts);
builder.setPorts(ports);
// Parse service registrations
final Map<ServiceEndpoint, ServicePorts> explicitRegistration = Maps.newHashMap();
final Pattern registrationPattern =
compile("(?<srv>[a-zA-Z][_\\-\\w]+)(?:/(?<prot>\\w+))?(?:=(?<port>[_\\-\\w]+))?");
final List<String> registrationSpecs = options.getList(registrationArg.getDest());
for (final String spec : registrationSpecs) {
final Matcher matcher = registrationPattern.matcher(spec);
if (!matcher.matches()) {
throw new IllegalArgumentException("Bad registration: " + spec);
}
final String service = matcher.group("srv");
final String proto = fromNullable(matcher.group("prot")).or(HTTP);
final String optionalPort = matcher.group("port");
final String port;
if (ports.size() == 0) {
throw new IllegalArgumentException("Need port mappings for service registration.");
}
if (optionalPort == null) {
if (ports.size() != 1) {
throw new IllegalArgumentException(
"Need exactly one port mapping for implicit service registration");
}
port = Iterables.getLast(ports.keySet());
} else {
port = optionalPort;
}
explicitRegistration.put(ServiceEndpoint.of(service, proto), ServicePorts.of(port));
}
// Merge service registrations
final Map<ServiceEndpoint, ServicePorts> registration = Maps.newHashMap();
registration.putAll(builder.getRegistration());
registration.putAll(explicitRegistration);
builder.setRegistration(registration);
// Get grace period interval
Integer gracePeriod = options.getInt(gracePeriodArg.getDest());
if (gracePeriod != null) {
builder.setGracePeriod(gracePeriod);
}
// Parse volumes
final List<String> volumeSpecs = options.getList(volumeArg.getDest());
for (final String spec : volumeSpecs) {
final String[] parts = spec.split(":", 2);
switch (parts.length) {
// Data volume
case 1:
builder.addVolume(parts[0]);
break;
// Bind mount
case 2:
final String path = parts[1];
final String source = parts[0];
builder.addVolume(path, source);
break;
default:
throw new IllegalArgumentException("Invalid volume: " + spec);
}
}
// Parse expires timestamp
final String expires = options.getString(expiresArg.getDest());
if (expires != null) {
// Use DateTime to parse the ISO-8601 string
builder.setExpires(new DateTime(expires).toDate());
}
final Job job = builder.build();
final Collection<String> errors = JOB_VALIDATOR.validate(job);
if (!errors.isEmpty()) {
if (!json) {
for (String error : errors) {
out.println(error);
}
} else {
CreateJobResponse createJobResponse = new CreateJobResponse(
CreateJobResponse.Status.INVALID_JOB_DEFINITION, null, job.getId().toString());
out.println(createJobResponse.toJsonString());
}
return 1;
}
if (!quiet && !json) {
out.println("Creating job: " + job.toJsonString());
}
final CreateJobResponse status = client.createJob(job).get();
if (status.getStatus() == CreateJobResponse.Status.OK) {
if (!quiet && !json) {
out.println("Done.");
}
if (json) {
out.println(status.toJsonString());
} else {
out.println(job.getId());
}
return 0;
} else {
if (!quiet && !json) {
out.println("Failed: " + status);