Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Inference API] Check pipelines on delete inference endpoint #109123

Merged
merged 20 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
aa28a7e
renaming
maxhniebergall May 15, 2024
a7727b6
check for pipeline references
maxhniebergall May 15, 2024
a58ff8f
add force and dry run options to request
maxhniebergall May 15, 2024
ac64d14
add force delete and dry run options to Actions
maxhniebergall May 28, 2024
290082e
Revert dependency on ml module
maxhniebergall May 28, 2024
31d57bf
duplicate InferenceProcessorInfoExtractor to inference module
maxhniebergall May 28, 2024
4d58493
Ignore missing IngestMetadata during attempted endpoint delete
maxhniebergall May 28, 2024
8ce76b8
Update docs/changelog/109123.yaml
maxhniebergall May 28, 2024
9318e22
rename RestDeleteInference Action
maxhniebergall May 28, 2024
3929cfd
fix dry run
maxhniebergall May 28, 2024
e5e62ef
Add integration test and misc improvements
maxhniebergall May 28, 2024
da3c3e2
Merge branch 'checkPipelinesOnDeleteInferenceEndpoint' of https://git…
maxhniebergall May 28, 2024
fcbf4c5
Update 109123.yaml
maxhniebergall May 28, 2024
9cdbc52
Merge branch 'main' into checkPipelinesOnDeleteInferenceEndpoint
maxhniebergall May 28, 2024
b7c3a1c
merge fixes
maxhniebergall May 28, 2024
73c9135
Improvements from PR review
maxhniebergall May 29, 2024
e688dea
Moving shared pieces to core (#109183)
jonathan-buttner May 29, 2024
038918c
replace `model` in exception message with `inference endpoint`
maxhniebergall May 30, 2024
30d4071
Merge branch 'main' into checkPipelinesOnDeleteInferenceEndpoint
maxhniebergall May 30, 2024
d1faacb
test fix
maxhniebergall May 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/109123.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 109123
summary: "[Inference API] Check for related pipelines on delete inference endpoint"
area: Machine Learning
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ static TransportVersion def(int id) {
public static final TransportVersion FAILURE_STORE_TELEMETRY = def(8_670_00_0);
public static final TransportVersion ADD_METADATA_FLATTENED_TO_ROLES = def(8_671_00_0);
public static final TransportVersion ML_INFERENCE_GOOGLE_AI_STUDIO_COMPLETION_ADDED = def(8_672_00_0);
public static final TransportVersion ML_INFERENCE_ENHANCE_DELETE_ENDPOINT = def(8_673_00_0);

/*
* STOP! READ THIS FIRST! No, really,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.inference.action;

import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.inference.TaskType;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Objects;
import java.util.Set;

public class DeleteInferenceEndpointAction extends ActionType<AcknowledgedResponse> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file already existed, it was just renamed


public static final DeleteInferenceEndpointAction INSTANCE = new DeleteInferenceEndpointAction();
public static final String NAME = "cluster:admin/xpack/inference/delete";

public DeleteInferenceEndpointAction() {
super(NAME);
}

public static class Request extends AcknowledgedRequest<DeleteInferenceEndpointAction.Request> {

private final String inferenceEndpointId;
private final TaskType taskType;
private final boolean forceDelete;
private final boolean dryRun;

public Request(String inferenceEndpointId, TaskType taskType, boolean forceDelete, boolean dryRun) {
super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT, DEFAULT_ACK_TIMEOUT);
this.inferenceEndpointId = inferenceEndpointId;
this.taskType = taskType;
this.forceDelete = forceDelete;
this.dryRun = dryRun;
}

public Request(StreamInput in) throws IOException {
super(in);
this.inferenceEndpointId = in.readString();
this.taskType = TaskType.fromStream(in);
if (in.getTransportVersion().onOrAfter(TransportVersions.ML_INFERENCE_ENHANCE_DELETE_ENDPOINT)) {
this.forceDelete = Boolean.TRUE.equals(in.readOptionalBoolean());
this.dryRun = Boolean.TRUE.equals(in.readOptionalBoolean());
} else {
this.forceDelete = false;
this.dryRun = false;
}
}

public String getInferenceEndpointId() {
return inferenceEndpointId;
}

public TaskType getTaskType() {
return taskType;
}

public boolean isForceDelete() {
return forceDelete;
}

public boolean isDryRun() {
return dryRun;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(inferenceEndpointId);
taskType.writeTo(out);
if (out.getTransportVersion().onOrAfter(TransportVersions.ML_INFERENCE_ENHANCE_DELETE_ENDPOINT)) {
out.writeOptionalBoolean(forceDelete);
out.writeOptionalBoolean(dryRun);
}
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DeleteInferenceEndpointAction.Request request = (DeleteInferenceEndpointAction.Request) o;
return Objects.equals(inferenceEndpointId, request.inferenceEndpointId)
&& taskType == request.taskType
&& forceDelete == request.forceDelete
&& dryRun == request.dryRun;
}

@Override
public int hashCode() {
return Objects.hash(inferenceEndpointId, taskType, forceDelete, dryRun);
}
}

public static class Response extends AcknowledgedResponse {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the main changes to this file are here, in the Response


private final String PIPELINE_IDS = "pipelines";
Set<String> pipelineIds;

public Response(boolean acknowledged, Set<String> pipelineIds) {
super(acknowledged);
this.pipelineIds = pipelineIds;
}

public Response(StreamInput in) throws IOException {
super(in);
pipelineIds = in.readCollectionAsSet(StreamInput::readString);
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeCollection(pipelineIds, StreamOutput::writeString);
}

@Override
protected void addCustomFields(XContentBuilder builder, Params params) throws IOException {
super.addCustomFields(builder, params);
builder.field(PIPELINE_IDS, pipelineIds);
}

@Override
public String toString() {
StringBuilder returnable = new StringBuilder();
returnable.append("acknowledged: ").append(this.acknowledged);
returnable.append(", pipelineIdsByEndpoint: ");
for (String entry : pipelineIds) {
returnable.append(entry).append(", ");
}
return returnable.toString();
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ protected void deleteModel(String modelId) throws IOException {
assertOkOrCreated(response);
}

protected Response deleteModel(String modelId, String queryParams) throws IOException {
var request = new Request("DELETE", "_inference/" + modelId + "?" + queryParams);
var response = client().performRequest(request);
assertOkOrCreated(response);
return response;
}

protected void deleteModel(String modelId, TaskType taskType) throws IOException {
var request = new Request("DELETE", Strings.format("_inference/%s/%s", taskType, modelId));
var response = client().performRequest(request);
Expand All @@ -124,6 +131,29 @@ protected Map<String, Object> putModel(String modelId, String modelConfig, TaskT
return putRequest(endpoint, modelConfig);
}

protected Map<String, Object> putPipeline(String pipelineId, String modelId) throws IOException {
String endpoint = Strings.format("_ingest/pipeline/%s", pipelineId);
String body = """
{
"description": "Test pipeline",
"processors": [
{
"inference": {
"model_id": "%s"
}
}
]
}
""".formatted(modelId);
return putRequest(endpoint, body);
}

protected void deletePipeline(String pipelineId) throws IOException {
var request = new Request("DELETE", Strings.format("_ingest/pipeline/%s", pipelineId));
var response = client().performRequest(request);
assertOkOrCreated(response);
}

/**
* Task type should be in modelConfig
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

package org.elasticsearch.xpack.inference;

import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.inference.TaskType;
Expand Down Expand Up @@ -115,4 +116,33 @@ public void testSkipValidationAndStart() throws IOException {
// We would expect an error about the invalid API key if the validation occurred
putModel("unvalidated", openAiConfigWithBadApiKey, TaskType.TEXT_EMBEDDING);
}

public void testDeleteEndpointWhileReferencedByPipeline() throws IOException {
String modelId = "model_referenced_by_pipeline";
putModel(modelId, mockSparseServiceModelConfig(), TaskType.SPARSE_EMBEDDING);
var pipelineId = "pipeline_referencing_model";
putPipeline(pipelineId, modelId);

{
var e = expectThrows(ResponseException.class, () -> deleteModel(modelId));
assertThat(
e.getMessage(),
containsString(
"Model model_referenced_by_pipeline is referenced by pipelines and cannot be deleted. Use `force` to delete it anyway, or use `dry_run` to list the pipelines that reference it."
)
);
}
{
var response = deleteModel(modelId, "dry_run=true");
var entityString = EntityUtils.toString(response.getEntity());
assertThat(entityString, containsString(pipelineId));
assertThat(entityString, containsString("\"acknowledged\":false"));
}
{
var response = deleteModel(modelId, "force=true");
var entityString = EntityUtils.toString(response.getEntity());
assertThat(entityString, containsString("\"acknowledged\":true"));
}
deletePipeline(pipelineId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
import org.elasticsearch.threadpool.ScalingExecutorBuilder;
import org.elasticsearch.xpack.core.ClientHelper;
import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction;
import org.elasticsearch.xpack.core.inference.action.DeleteInferenceModelAction;
import org.elasticsearch.xpack.core.inference.action.DeleteInferenceEndpointAction;
import org.elasticsearch.xpack.core.inference.action.GetInferenceModelAction;
import org.elasticsearch.xpack.core.inference.action.InferenceAction;
import org.elasticsearch.xpack.core.inference.action.PutInferenceModelAction;
import org.elasticsearch.xpack.inference.action.TransportDeleteInferenceModelAction;
import org.elasticsearch.xpack.inference.action.TransportDeleteInferenceEndpointAction;
import org.elasticsearch.xpack.inference.action.TransportGetInferenceModelAction;
import org.elasticsearch.xpack.inference.action.TransportInferenceAction;
import org.elasticsearch.xpack.inference.action.TransportInferenceUsageAction;
Expand All @@ -58,7 +58,7 @@
import org.elasticsearch.xpack.inference.mapper.SemanticTextFieldMapper;
import org.elasticsearch.xpack.inference.queries.SemanticQueryBuilder;
import org.elasticsearch.xpack.inference.registry.ModelRegistry;
import org.elasticsearch.xpack.inference.rest.RestDeleteInferenceModelAction;
import org.elasticsearch.xpack.inference.rest.RestDeleteInferenceEndpointAction;
import org.elasticsearch.xpack.inference.rest.RestGetInferenceModelAction;
import org.elasticsearch.xpack.inference.rest.RestInferenceAction;
import org.elasticsearch.xpack.inference.rest.RestPutInferenceModelAction;
Expand Down Expand Up @@ -122,7 +122,7 @@ public InferencePlugin(Settings settings) {
new ActionHandler<>(InferenceAction.INSTANCE, TransportInferenceAction.class),
new ActionHandler<>(GetInferenceModelAction.INSTANCE, TransportGetInferenceModelAction.class),
new ActionHandler<>(PutInferenceModelAction.INSTANCE, TransportPutInferenceModelAction.class),
new ActionHandler<>(DeleteInferenceModelAction.INSTANCE, TransportDeleteInferenceModelAction.class),
new ActionHandler<>(DeleteInferenceEndpointAction.INSTANCE, TransportDeleteInferenceEndpointAction.class),
new ActionHandler<>(XPackUsageFeatureAction.INFERENCE, TransportInferenceUsageAction.class)
);
}
Expand All @@ -143,7 +143,7 @@ public List<RestHandler> getRestHandlers(
new RestInferenceAction(),
new RestGetInferenceModelAction(),
new RestPutInferenceModelAction(),
new RestDeleteInferenceModelAction()
new RestDeleteInferenceEndpointAction()
);
}

Expand Down
Loading
Loading