Skip to content

Commit

Permalink
feat: get all services (#2367)
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?
YES

### References
#2349
  • Loading branch information
tedim52 committed Apr 15, 2024
1 parent dd8d1ff commit 2ba98f5
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 9 deletions.
Expand Up @@ -34,3 +34,19 @@ func (itvs *InterpretationTimeValueStore) GetService(name service.ServiceName) (
}
return serviceStarlark, nil
}

func (itvs *InterpretationTimeValueStore) GetServices() ([]*kurtosis_types.Service, error) {
servicesStarlark, err := itvs.serviceValues.GetServices()
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred fetching interpretation time service objects from db")
}
return servicesStarlark, nil
}

func (itvs *InterpretationTimeValueStore) RemoveService(name service.ServiceName) error {
err := itvs.serviceValues.RemoveService(name)
if err != nil {
return stacktrace.Propagate(err, "An error occurred removing interpretation time service object for service '%v'", name)
}
return nil
}
Expand Up @@ -105,7 +105,49 @@ func (repository *serviceInterpretationValueRepository) GetService(name service.
}
logrus.Debugf("Successfully got value for '%v'", name)
return value, nil
}

func (repository *serviceInterpretationValueRepository) GetServices() ([]*kurtosis_types.Service, error) {
logrus.Debug("Getting all known interpretation time service values.")
var services []*kurtosis_types.Service

if err := repository.enclaveDb.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(serviceInterpretationValueBucketName)

return bucket.ForEach(func(serviceName, serializedValue []byte) error {
deserializedValue, interpretationErr := repository.starlarkValueSerde.Deserialize(string(serializedValue))
if interpretationErr != nil {
return stacktrace.Propagate(interpretationErr, "an error occurred while deserializing object associated with service '%v' in repository", serviceName)
}

kurtosisServiceValue, ok := deserializedValue.(*kurtosis_types.Service)
if !ok {
return stacktrace.NewError("an error occurred casting repository service value to kurtosis service value for service: %v", serviceName)
}

services = append(services, kurtosisServiceValue)
return nil
})
}); err != nil {
return nil, stacktrace.Propagate(err, "An error occurred while getting services values from repository.")
}
logrus.Debugf("Successfully retrieved interpretation time service values.")
return services, nil
}

func (repository *serviceInterpretationValueRepository) RemoveService(name service.ServiceName) error {
logrus.Debugf("Removing service value for '%v' from service value repository...", name)
if err := repository.enclaveDb.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(serviceInterpretationValueBucketName)

serviceNameKey := getKey(name)

return bucket.Delete(serviceNameKey)
}); err != nil {
return stacktrace.Propagate(err, "An error occurred while removing service '%v' from service value repository", name)
}
logrus.Debugf("Successfully removed service value for '%v'", name)
return nil
}

func getKey(name service.ServiceName) []byte {
Expand Down
@@ -1,6 +1,7 @@
package interpretation_time_value_store

import (
"fmt"
port_spec_core "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/database_accessors/enclave_db"
Expand All @@ -16,7 +17,7 @@ import (

const (
starlarkThreadName = "thread-for-db-test"
serviceName = service.ServiceName("datastore-1")
serviceName = service.ServiceName("datastore")
serviceNameStarlarkStr = starlark.String(serviceName)
hostName = serviceNameStarlarkStr
ipAddress = starlark.String("172.23.34.44")
Expand Down Expand Up @@ -62,6 +63,84 @@ func TestPutGetFail_ForMissingServiceName(t *testing.T) {
require.Nil(t, actualService)
}

func TestGetServices(t *testing.T) {
repository := getServiceInterpretationTimeValueRepository(t)
require.NotNil(t, repository)

applicationProtocol := ""
maybeUrl := ""

port, interpretationErr := port_spec.CreatePortSpecUsingGoValues(
string(serviceName),
uint16(443),
port_spec_core.TransportProtocol_TCP,
&applicationProtocol,
"10s",
&maybeUrl,
)
require.Nil(t, interpretationErr)
ports := starlark.NewDict(1)
require.NoError(t, ports.SetKey(starlark.String("http"), port))

serviceOneName := fmt.Sprintf("%v-%v", serviceName, "1")
serviceOne, interpretationErr := kurtosis_types.CreateService(starlark.String(serviceOneName), hostName, ipAddress, ports)
require.Nil(t, interpretationErr)

serviceTwoName := fmt.Sprintf("%v-%v", serviceName, "2")
serviceTwo, interpretationErr := kurtosis_types.CreateService(starlark.String(serviceTwoName), hostName, ipAddress, ports)
require.Nil(t, interpretationErr)

err := repository.PutService(service.ServiceName(serviceOneName), serviceOne)
require.Nil(t, err)
err = repository.PutService(service.ServiceName(serviceTwoName), serviceTwo)
require.Nil(t, err)

actualServices, err := repository.GetServices()
require.NoError(t, err)
require.Len(t, actualServices, 2)

actualServiceOneName, err := actualServices[0].GetName()
require.Nil(t, err)
require.Equal(t, service.ServiceName(serviceOneName), actualServiceOneName)

actualServiceTwoName, err := actualServices[1].GetName()
require.Nil(t, err)
require.Equal(t, service.ServiceName(serviceTwoName), actualServiceTwoName)
}

func TestRemoveService(t *testing.T) {
repository := getServiceInterpretationTimeValueRepository(t)
require.NotNil(t, repository)

applicationProtocol := ""
maybeUrl := ""

port, interpretationErr := port_spec.CreatePortSpecUsingGoValues(
string(serviceName),
uint16(443),
port_spec_core.TransportProtocol_TCP,
&applicationProtocol,
"10s",
&maybeUrl,
)
require.Nil(t, interpretationErr)
ports := starlark.NewDict(1)
require.NoError(t, ports.SetKey(starlark.String("http"), port))

serviceOneName := fmt.Sprintf("%v-%v", serviceName, "1")
serviceOne, interpretationErr := kurtosis_types.CreateService(starlark.String(serviceOneName), hostName, ipAddress, ports)
require.Nil(t, interpretationErr)

err := repository.PutService(service.ServiceName(serviceOneName), serviceOne)
require.Nil(t, err)

err = repository.RemoveService(service.ServiceName(serviceOneName))
require.Nil(t, err)

_, err = repository.GetService(service.ServiceName(serviceOneName))
require.Error(t, err)
}

func getServiceInterpretationTimeValueRepository(t *testing.T) *serviceInterpretationValueRepository {
file, err := os.CreateTemp("/tmp", "*.db")
defer func() {
Expand Down
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/get_files_artifact"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/get_service"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/get_services"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates"
Expand Down Expand Up @@ -71,11 +72,12 @@ func KurtosisPlanInstructions(
add_service.NewAddService(serviceNetwork, runtimeValueStore, packageId, packageContentProvider, packageReplaceOptions, interpretationTimeValueStore, imageDownloadMode),
add_service.NewAddServices(serviceNetwork, runtimeValueStore, packageId, packageContentProvider, packageReplaceOptions, interpretationTimeValueStore, imageDownloadMode),
get_service.NewGetService(interpretationTimeValueStore),
get_services.NewGetServices(interpretationTimeValueStore),
get_files_artifact.NewGetFilesArtifact(),
verify.NewVerify(runtimeValueStore),
exec.NewExec(serviceNetwork, runtimeValueStore),
kurtosis_print.NewPrint(serviceNetwork, runtimeValueStore),
remove_service.NewRemoveService(serviceNetwork),
remove_service.NewRemoveService(serviceNetwork, interpretationTimeValueStore),
render_templates.NewRenderTemplatesInstruction(serviceNetwork, runtimeValueStore),
request.NewRequest(serviceNetwork, runtimeValueStore),
start_service.NewStartService(serviceNetwork),
Expand Down
@@ -0,0 +1,101 @@
package get_services

import (
"context"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_plan_persistence"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/interpretation_time_value_store"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/plan_yaml"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_validator"
"go.starlark.net/starlark"
)

const (
GetServicesBuiltinName = "get_services"
descriptionStr = "Fetching services"
)

func NewGetServices(interpretationTimeStore *interpretation_time_value_store.InterpretationTimeValueStore) *kurtosis_plan_instruction.KurtosisPlanInstruction {
return &kurtosis_plan_instruction.KurtosisPlanInstruction{
KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{
Name: GetServicesBuiltinName,
Arguments: []*builtin_argument.BuiltinArgument{},
Deprecation: nil,
},
Capabilities: func() kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities {
return &GetServicesCapabilities{
interpretationTimeStore: interpretationTimeStore,
serviceNames: []service.ServiceName{}, // populated at interpretation time
description: "", // populated at interpretation time
}
},
DefaultDisplayArguments: map[string]bool{},
}
}

type GetServicesCapabilities struct {
interpretationTimeStore *interpretation_time_value_store.InterpretationTimeValueStore
serviceNames []service.ServiceName
description string
}

func (builtin *GetServicesCapabilities) Interpret(_ string, arguments *builtin_argument.ArgumentValuesSet) (starlark.Value, *startosis_errors.InterpretationError) {
builtin.description = builtin_argument.GetDescriptionOrFallBack(arguments, descriptionStr)

services, err := builtin.interpretationTimeStore.GetServices()
if err != nil {
return nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred while fetching service.")
}
servicesList := &starlark.List{}
for _, serviceVal := range services {
name, err := serviceVal.GetName()
if err != nil {
return nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred getting name of service: %v", serviceVal)
}
builtin.serviceNames = append(builtin.serviceNames, name)

_ = servicesList.Append(serviceVal)
}

return servicesList, nil
}

func (builtin *GetServicesCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
// validate if all services exist in the validation environment
for _, serviceName := range builtin.serviceNames {
if exists := validatorEnvironment.DoesServiceNameExist(serviceName); exists == startosis_validator.ComponentNotFound {
return startosis_errors.NewValidationError("Service '%v' required by '%v' instruction doesn't exist", serviceName, GetServicesBuiltinName)
}
}
return nil
}

func (builtin *GetServicesCapabilities) Execute(_ context.Context, _ *builtin_argument.ArgumentValuesSet) (string, error) {
// note: this is a no op
return descriptionStr, nil
}

func (builtin *GetServicesCapabilities) TryResolveWith(instructionsAreEqual bool, _ *enclave_plan_persistence.EnclavePlanInstruction, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
if instructionsAreEqual {
return enclave_structure.InstructionIsEqual
}
return enclave_structure.InstructionIsUnknown
}

func (builtin *GetServicesCapabilities) FillPersistableAttributes(builder *enclave_plan_persistence.EnclavePlanInstructionBuilder) {
builder.SetType(GetServicesBuiltinName)
}

func (builtin *GetServicesCapabilities) UpdatePlan(planYaml *plan_yaml.PlanYaml) error {
// get services does not affect the plan
return nil
}

func (builtin *GetServicesCapabilities) Description() string {
return builtin.description
}
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_plan_persistence"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/interpretation_time_value_store"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
Expand All @@ -24,7 +25,7 @@ const (
descriptionFormatStr = "Removing service '%v'"
)

func NewRemoveService(serviceNetwork service_network.ServiceNetwork) *kurtosis_plan_instruction.KurtosisPlanInstruction {
func NewRemoveService(serviceNetwork service_network.ServiceNetwork, interpretationTimeStore *interpretation_time_value_store.InterpretationTimeValueStore) *kurtosis_plan_instruction.KurtosisPlanInstruction {
return &kurtosis_plan_instruction.KurtosisPlanInstruction{
KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{
Name: RemoveServiceBuiltinName,
Expand All @@ -44,7 +45,8 @@ func NewRemoveService(serviceNetwork service_network.ServiceNetwork) *kurtosis_p

Capabilities: func() kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities {
return &RemoveServiceCapabilities{
serviceNetwork: serviceNetwork,
serviceNetwork: serviceNetwork,
interpretationTimeStore: interpretationTimeStore,

serviceName: "", // populated at interpretation time
description: "", // populated at interpretation time
Expand All @@ -58,7 +60,8 @@ func NewRemoveService(serviceNetwork service_network.ServiceNetwork) *kurtosis_p
}

type RemoveServiceCapabilities struct {
serviceNetwork service_network.ServiceNetwork
serviceNetwork service_network.ServiceNetwork
interpretationTimeStore *interpretation_time_value_store.InterpretationTimeValueStore

serviceName service.ServiceName
description string
Expand All @@ -71,6 +74,10 @@ func (builtin *RemoveServiceCapabilities) Interpret(_ string, arguments *builtin
}

builtin.serviceName = service.ServiceName(serviceName.GoString())
err = builtin.interpretationTimeStore.RemoveService(builtin.serviceName)
if err != nil {
return nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred removing '%v' from interpretation time store", builtin.serviceName)
}
builtin.description = builtin_argument.GetDescriptionOrFallBack(arguments, fmt.Sprintf(descriptionFormatStr, builtin.serviceName))
return starlark.None, nil
}
Expand Down
Expand Up @@ -3,6 +3,7 @@ package test_engine
import (
"fmt"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/interpretation_time_value_store"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service"
"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
"github.com/stretchr/testify/mock"
Expand All @@ -13,7 +14,8 @@ import (

type removeServiceTestCase struct {
*testing.T
serviceNetwork *service_network.MockServiceNetwork
serviceNetwork *service_network.MockServiceNetwork
interpretationTimeStore *interpretation_time_value_store.InterpretationTimeValueStore
}

func (suite *KurtosisPlanInstructionTestSuite) TestRemoveService() {
Expand All @@ -26,13 +28,14 @@ func (suite *KurtosisPlanInstructionTestSuite) TestRemoveService() {
)

suite.run(&removeServiceTestCase{
T: suite.T(),
serviceNetwork: suite.serviceNetwork,
T: suite.T(),
serviceNetwork: suite.serviceNetwork,
interpretationTimeStore: suite.interpretationTimeValueStore,
})
}

func (t *removeServiceTestCase) GetInstruction() *kurtosis_plan_instruction.KurtosisPlanInstruction {
return remove_service.NewRemoveService(t.serviceNetwork)
return remove_service.NewRemoveService(t.serviceNetwork, t.interpretationTimeStore)
}

func (t *removeServiceTestCase) GetStarlarkCode() string {
Expand Down

0 comments on commit 2ba98f5

Please sign in to comment.