use of com.netflix.titus.api.jobmanager.model.job.ext.ServiceJobExt in project titus-control-plane by Netflix.
the class MoveTaskTest method testMoveWithIncompatibleTargetJob.
@Test
public void testMoveWithIncompatibleTargetJob() {
JobDescriptor<ServiceJobExt> jobDescriptor = oneTaskServiceJobDescriptor();
JobScenarioBuilder sourceJobBuilder = startNewJob(jobDescriptor);
String sourceJobId = sourceJobBuilder.getJobId();
JobDescriptor<ServiceJobExt> incompatible = jobDescriptor.but(descriptor -> descriptor.getContainer().but(container -> container.getImage().toBuilder().withName("other/image").build()));
String targetJobId = startNewJob(incompatible).getJobId();
try {
sourceJobBuilder.moveTask(0, 0, sourceJobId, targetJobId);
} catch (JobManagerException e) {
assertThat(e.getErrorCode()).isEqualTo(JobManagerException.ErrorCode.JobsNotCompatible);
assertThat(e.getMessage()).contains("container.image.name");
}
}
use of com.netflix.titus.api.jobmanager.model.job.ext.ServiceJobExt in project titus-control-plane by Netflix.
the class V1SpecPodFactoryTest method relocationLabel.
@Test
public void relocationLabel() {
Job<ServiceJobExt> job = JobGenerator.oneServiceJob();
Job<ServiceJobExt> selfManagedJob = job.toBuilder().withJobDescriptor(job.getJobDescriptor().but(jd -> jd.getDisruptionBudget().toBuilder().withDisruptionBudgetPolicy(SelfManagedDisruptionBudgetPolicy.newBuilder().build()))).build();
ServiceJobTask task = JobGenerator.oneServiceTask();
when(podAffinityFactory.buildV1Affinity(any(), eq(task))).thenReturn(Pair.of(new V1Affinity(), new HashMap<>()));
V1Pod pod = podFactory.buildV1Pod(job, task);
assertThat(pod.getMetadata().getLabels()).doesNotContainKey(KubeConstants.POD_LABEL_RELOCATION_BINPACK);
V1Pod selfManagedPod = podFactory.buildV1Pod(selfManagedJob, task);
assertThat(selfManagedPod.getMetadata().getLabels()).containsEntry(KubeConstants.POD_LABEL_RELOCATION_BINPACK, "SelfManaged");
}
use of com.netflix.titus.api.jobmanager.model.job.ext.ServiceJobExt in project titus-control-plane by Netflix.
the class DefaultDeschedulerServiceTest method verifyRelocationPlan.
private void verifyRelocationPlan(long relocationDelay, String reasonMessage) {
ReadOnlyJobOperations jobOperations = mock(ReadOnlyJobOperations.class);
DefaultDeschedulerService dds = new DefaultDeschedulerService(jobOperations, mock(ReadOnlyEvictionOperations.class), new KubernetesNodeDataResolver(configuration, TestDataFactory.mockFabric8IOConnector(), node -> true), () -> "foo|bar", titusRuntime);
Job<ServiceJobExt> job = JobGenerator.serviceJobs(oneTaskServiceJobDescriptor().but(ofServiceSize(2), withDisruptionBudget(budget(selfManagedPolicy(relocationDelay), unlimitedRate(), Collections.emptyList())))).getValue();
ServiceJobTask task = JobGenerator.serviceTasks(job).getValue();
when(jobOperations.getJob(job.getId())).thenReturn(Optional.of(job));
TitusNode node = TitusNode.newBuilder().withId("node1").withServerGroupId("asg1").withRelocationRequired(true).withBadCondition(false).build();
// Advance test clock
long clockAdvancedMs = 5_000;
TestClock testClock = (TestClock) titusRuntime.getClock();
testClock.advanceTime(Duration.ofMillis(clockAdvancedMs));
Optional<TaskRelocationPlan> relocationPlanForTask = dds.getRelocationPlanForTask(node, task, Collections.emptyMap());
assertThat(relocationPlanForTask).isPresent();
assertThat(relocationPlanForTask.get().getTaskId()).isEqualTo(task.getId());
// relocation time is expected to be decision clock time + retentionTimeMs
assertThat(relocationPlanForTask.get().getRelocationTime()).isEqualTo(relocationDelay + clockAdvancedMs);
assertThat(relocationPlanForTask.get().getDecisionTime()).isEqualTo(clockAdvancedMs);
assertThat(relocationPlanForTask.get().getReasonMessage()).isEqualTo(reasonMessage);
}
use of com.netflix.titus.api.jobmanager.model.job.ext.ServiceJobExt in project titus-control-plane by Netflix.
the class ExtendedJobSanitizerTest method testLegacyServiceJobDisruptionBudgetRewrite.
@Test
public void testLegacyServiceJobDisruptionBudgetRewrite() {
JobDescriptor<ServiceJobExt> jobDescriptor = newServiceJob().getValue().toBuilder().withDisruptionBudget(DisruptionBudget.none()).build();
ExtendedJobSanitizer sanitizer = new ExtendedJobSanitizer(configuration, jobAssertions, entitySanitizer, disruptionBudgetSanitizer, jd -> false, jd -> false, titusRuntime);
Optional<JobDescriptor<ServiceJobExt>> sanitizedOpt = sanitizer.sanitize(jobDescriptor);
assertThat(sanitizedOpt).isNotEmpty();
JobDescriptor<ServiceJobExt> sanitized = sanitizedOpt.get();
String nonCompliant = sanitized.getAttributes().get(TITUS_NON_COMPLIANT_FEATURES);
assertThat(nonCompliant).contains(JobFeatureComplianceChecks.DISRUPTION_BUDGET_FEATURE);
SelfManagedDisruptionBudgetPolicy policy = (SelfManagedDisruptionBudgetPolicy) sanitized.getDisruptionBudget().getDisruptionBudgetPolicy();
assertThat(policy.getRelocationTimeMs()).isEqualTo(DisruptionBudgetSanitizer.DEFAULT_SERVICE_RELOCATION_TIME_MS);
}
use of com.netflix.titus.api.jobmanager.model.job.ext.ServiceJobExt in project titus-control-plane by Netflix.
the class CassandraJobStore method retrieveJobs.
@Override
public Observable<Pair<List<Job<?>>, Integer>> retrieveJobs() {
Observable result = Observable.fromCallable(() -> {
List<String> jobIds = activeJobIdsBucketManager.getItems();
return jobIds.stream().map(retrieveActiveJobStatement::bind).map(this::execute).collect(Collectors.toList());
}).flatMap(observables -> Observable.merge(observables, getConcurrencyLimit()).flatMapIterable(resultSet -> {
List<Row> allRows = resultSet.all();
if (allRows.isEmpty()) {
logger.debug("Job id with no record");
return Collections.emptyList();
}
return allRows.stream().map(row -> row.getString(0)).map(value -> {
String effectiveValue;
if (fitBadDataInjection.isPresent()) {
effectiveValue = fitBadDataInjection.get().afterImmediate(JobStoreFitAction.ErrorKind.CorruptedRawJobRecords.name(), value);
} else {
effectiveValue = value;
}
Job<?> job;
try {
job = deserializeJob(effectiveValue);
} catch (Exception e) {
logger.error("Cannot map serialized job data to Job class: {}", effectiveValue, e);
return Either.ofError(e);
}
if (job.getJobDescriptor().getDisruptionBudget() == null) {
titusRuntime.getCodeInvariants().inconsistent("jobWithNoDisruptionBudget: jobId=%s", job.getId());
job = JobFunctions.changeDisruptionBudget(job, DisruptionBudget.none());
}
// TODO Remove this code when there are no more jobs with missing migration data (caused by a bug in ServiceJobExt builder).
if (job.getJobDescriptor().getExtensions() instanceof ServiceJobExt) {
Job<ServiceJobExt> serviceJob = (Job<ServiceJobExt>) job;
ServiceJobExt ext = serviceJob.getJobDescriptor().getExtensions();
if (ext.getMigrationPolicy() == null) {
titusRuntime.getCodePointTracker().markReachable("Corrupted task migration record in Cassandra: " + job.getId());
ServiceJobExt fixedExt = ext.toBuilder().withMigrationPolicy(SystemDefaultMigrationPolicy.newBuilder().build()).build();
logger.warn("Service job with no migration policy defined. Setting system default: {}", job.getId());
job = serviceJob.toBuilder().withJobDescriptor(serviceJob.getJobDescriptor().toBuilder().withExtensions(fixedExt).build()).build();
}
}
if (!fitBadDataInjection.isPresent()) {
return Either.ofValue(job);
}
Job<?> effectiveJob = fitBadDataInjection.get().afterImmediate(JobStoreFitAction.ErrorKind.CorruptedJobRecords.name(), job);
return Either.ofValue(effectiveJob);
}).collect(Collectors.toList());
})).toList().map(everything -> {
List<Job> goodJobs = (List<Job>) everything.stream().filter(Either::hasValue).map(Either::getValue).collect(Collectors.toList());
int errors = everything.size() - goodJobs.size();
return Pair.of(goodJobs, errors);
});
return result;
}
Aggregations