Skip to content

Commit

Permalink
feat: Add ability to update a running service (#943)
Browse files Browse the repository at this point in the history
## Description:
<!-- Describe this change, how it works, and the motivation behind it.
-->

## Is this change user facing?
NO
<!-- If yes, please add the "user facing" label to the PR -->
<!-- If yes, don't forget to include docs changes where relevant -->

## References (if applicable):
<!-- Add relevant Github Issues, Discord threads, or other helpful
information. -->
  • Loading branch information
Guillaume Bouvignies committed Jul 20, 2023
1 parent ccb4a81 commit 42a67f9
Show file tree
Hide file tree
Showing 14 changed files with 698 additions and 29 deletions.
Expand Up @@ -227,6 +227,28 @@ func (backend *DockerKurtosisBackend) StartRegisteredUserServices(ctx context.Co
return successfullyStartedService, failedService, nil
}

func (backend *DockerKurtosisBackend) RemoveRegisteredUserServiceProcesses(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceUUID]bool) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) {
serviceRegistrationsForEnclave, found := backend.serviceRegistrations[enclaveUuid]
if !found {
return nil, nil, stacktrace.NewError(
"No service registrations are being tracked for enclave '%v'; this likely means that the registration "+
"request is being called where it shouldn't be (i.e. outside the API container)",
enclaveUuid,
)
}

successfullyStartedService, failedService, err := user_service_functions.RemoveRegisteredUserServiceProcesses(
ctx,
enclaveUuid,
services,
serviceRegistrationsForEnclave,
backend.dockerManager)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "Unexpected error while updating user services")
}
return successfullyStartedService, failedService, nil
}

func (backend *DockerKurtosisBackend) GetUserServices(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
Expand Down
Expand Up @@ -196,6 +196,88 @@ func StartRegisteredUserServices(
return successfulServicesPool, failedServicesPool, nil
}

func RemoveRegisteredUserServiceProcesses(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
services map[service.ServiceUUID]bool,
serviceRegistrations map[service.ServiceUUID]*service.ServiceRegistration,
dockerManager *docker_manager.DockerManager,
) (
map[service.ServiceUUID]bool,
map[service.ServiceUUID]error,
error,
) {
successfullyRemovedService := map[service.ServiceUUID]bool{}
failedServicesPool := map[service.ServiceUUID]error{}

serviceUuidsToUpdate := map[service.ServiceUUID]bool{}
for serviceUuid := range services {
if _, found := serviceRegistrations[serviceUuid]; found {
serviceUuidsToUpdate[serviceUuid] = true
} else {
failedServicesPool[serviceUuid] = stacktrace.NewError("Unable to update service '%s' that is not "+
"registered inside this enclave", serviceUuid)
}
}

removeServiceFilters := &service.ServiceFilters{
Names: nil,
UUIDs: serviceUuidsToUpdate,
Statuses: nil,
}

allServiceObjs, allDockerResources, err := shared_helpers.GetMatchingUserServiceObjsAndDockerResourcesNoMutex(ctx, enclaveUuid, removeServiceFilters, dockerManager)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "An error occurred getting user services matching filters '%+v'", removeServiceFilters)
}

servicesToStartByContainerId := map[string]*service.Service{}
for uuid, serviceResources := range allDockerResources {
serviceObj, found := allServiceObjs[uuid]
if !found {
// Should never happen; there should be a 1:1 mapping between service_objects:docker_resources by GUID
return nil, nil, stacktrace.NewError("No service object found for service '%v' that had Docker resources; this is a bug in Kurtosis", uuid)
}
servicesToStartByContainerId[serviceResources.ServiceContainer.GetId()] = serviceObj
}

var dockerOperation docker_operation_parallelizer.DockerOperation = func(
ctx context.Context,
dockerManager *docker_manager.DockerManager,
dockerObjectId string,
) error {
if err := dockerManager.RemoveContainer(ctx, dockerObjectId); err != nil {
return stacktrace.Propagate(err, "An error occurred removing user service processes container with ID '%v'", dockerObjectId)
}
return nil
}

successfulUuidStrs, erroredUuidStrs, err := docker_operation_parallelizer.RunDockerOperationInParallelForKurtosisObjects(
ctx,
servicesToStartByContainerId,
dockerManager,
extractServiceUUIDFromService,
dockerOperation,
)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "An error occurred removing user service processes containers matching filters '%+v'", removeServiceFilters)
}

for failedServiceUuid, failedServiceErr := range erroredUuidStrs {
failedServicesPool[service.ServiceUUID(failedServiceUuid)] = failedServiceErr
}

for serviceUuidStr := range successfulUuidStrs {
serviceUuid := service.ServiceUUID(serviceUuidStr)
serviceConfig, found := services[serviceUuid]
if !found {
failedServicesPool[serviceUuid] = stacktrace.NewError("An error occurred removing user service processes for service with UUID '%s'", serviceUuid)
}
successfullyRemovedService[serviceUuid] = serviceConfig
}
return successfullyRemovedService, failedServicesPool, nil
}

// ====================================================================================================
//
// Private helper functions
Expand Down
Expand Up @@ -263,6 +263,33 @@ func (backend *KubernetesKurtosisBackend) StartRegisteredUserServices(
return successfullyStartedServices, failedServices, nil
}

func (backend *KubernetesKurtosisBackend) RemoveRegisteredUserServiceProcesses(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
services map[service.ServiceUUID]bool,
) (
map[service.ServiceUUID]bool,
map[service.ServiceUUID]error,
error,
) {
successfullyStartedServices, failedServices, err := user_services_functions.RemoveRegisteredUserServiceProcesses(
ctx,
enclaveUuid,
services,
backend.cliModeArgs,
backend.apiContainerModeArgs,
backend.engineServerModeArgs,
backend.kubernetesManager)
if err != nil {
var serviceUuids []service.ServiceUUID
for serviceUuid := range services {
serviceUuids = append(serviceUuids, serviceUuid)
}
return nil, nil, stacktrace.Propagate(err, "Unexpected error removing services with GUIDs '%v' in enclave '%s'", serviceUuids, enclaveUuid)
}
return successfullyStartedServices, failedServices, nil
}

func (backend *KubernetesKurtosisBackend) GetUserServices(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
Expand Down
Expand Up @@ -193,6 +193,39 @@ func StartRegisteredUserServices(
return successfulServicesPool, failedServicesPool, nil
}

func RemoveRegisteredUserServiceProcesses(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
services map[service.ServiceUUID]bool,
cliModeArgs *shared_helpers.CliModeArgs,
apiContainerModeArgs *shared_helpers.ApiContainerModeArgs,
engineServerModeArgs *shared_helpers.EngineServerModeArgs,
kubernetesManager *kubernetes_manager.KubernetesManager,
) (
map[service.ServiceUUID]bool,
map[service.ServiceUUID]error,
error,
) {
// in Kubernetes, removing service process is equivalent to stopping it. It sets its number of pod to zero
removeServiceProcessesFilters := &service.ServiceFilters{
Names: nil,
UUIDs: services,
Statuses: nil,
}
successfullyRemovedService, failedRemovedService, err := StopUserServices(
ctx,
enclaveUuid,
removeServiceProcessesFilters,
cliModeArgs,
apiContainerModeArgs,
engineServerModeArgs,
kubernetesManager)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "Unexpected error removing service processes")
}
return successfullyRemovedService, failedRemovedService, nil
}

// ====================================================================================================
//
// Private helper functions
Expand Down
Expand Up @@ -257,6 +257,14 @@ func (backend *MetricsReportingKurtosisBackend) StartRegisteredUserServices(ctx
return successes, failures, nil
}

func (backend *MetricsReportingKurtosisBackend) RemoveRegisteredUserServiceProcesses(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceUUID]bool) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) {
successes, failures, err := backend.underlying.RemoveRegisteredUserServiceProcesses(ctx, enclaveUuid, services)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "An error occurred removing service processes in enclave '%v' with the following service ids: %+v", enclaveUuid, services)
}
return successes, failures, nil
}

func (backend *MetricsReportingKurtosisBackend) GetUserServices(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
Expand Down
Expand Up @@ -136,6 +136,10 @@ func (backend *RemoteContextKurtosisBackend) StartRegisteredUserServices(ctx con
return backend.remoteKurtosisBackend.StartRegisteredUserServices(ctx, enclaveUuid, services)
}

func (backend *RemoteContextKurtosisBackend) RemoveRegisteredUserServiceProcesses(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceUUID]bool) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) {
return backend.remoteKurtosisBackend.RemoveRegisteredUserServiceProcesses(ctx, enclaveUuid, services)
}

func (backend *RemoteContextKurtosisBackend) GetUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, filters *service.ServiceFilters) (map[service.ServiceUUID]*service.Service, error) {
return backend.remoteKurtosisBackend.GetUserServices(ctx, enclaveUuid, filters)
}
Expand Down
13 changes: 13 additions & 0 deletions container-engine-lib/lib/backend_interface/kurtosis_backend.go
Expand Up @@ -223,6 +223,19 @@ type KurtosisBackend interface {
error, // represents an error with the function itself, rather than the user services
)

// RemoveRegisteredUserServiceProcesses removes the running user service process but keeps the service registration
// TODO: As we don't persist user service logs anywhere, removing the container/pod will also remove all its
// logs. We need a persistent log storage to address this issue.
RemoveRegisteredUserServiceProcesses(
ctx context.Context,
enclaveUuid enclave.EnclaveUUID,
services map[service.ServiceUUID]bool,
) (
map[service.ServiceUUID]bool, // user service UUIDs that were successfully removed
map[service.ServiceUUID]error, // user service UUIDs that failed to be removed, with the error
error, // represents an error with the function itself, rather than the user services
)

// Gets user services using the given filters, returning a map of matched user services identified by their UUID
GetUserServices(
ctx context.Context,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 42a67f9

Please sign in to comment.