package service import ( "context" "fmt" "strings" "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/log" "github.com/AlecAivazis/survey/v2" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" ) // GetService retrieves a service container based on a label. If prompt is true // and the retrievd count of service containers does not match 1, then a prompt // is presented to let the user choose. An error is returned when no service is // found. func GetServiceByLabel(c context.Context, cl *client.Client, label string, prompt bool) (swarm.Service, error) { services, err := cl.ServiceList(c, types.ServiceListOptions{}) if err != nil { return swarm.Service{}, err } if len(services) == 0 { return swarm.Service{}, fmt.Errorf("no services deployed?") } var matchingServices []swarm.Service for _, service := range services { if enabled, exists := service.Spec.Labels[label]; exists && enabled == "true" { matchingServices = append(matchingServices, service) } } if len(matchingServices) == 0 { return swarm.Service{}, fmt.Errorf("no services deployed matching label '%s'?", label) } if len(matchingServices) > 1 { var servicesRaw []string for _, service := range matchingServices { serviceName := service.Spec.Name created := formatter.HumanDuration(service.CreatedAt.Unix()) servicesRaw = append(servicesRaw, fmt.Sprintf("%s (created %v)", serviceName, created)) } if !prompt { err := fmt.Errorf("expected 1 service but found %v: %s", len(matchingServices), strings.Join(servicesRaw, " ")) return swarm.Service{}, err } log.Warnf("ambiguous service list received, prompting for input") var response string prompt := &survey.Select{ Message: "which service are you looking for?", Options: servicesRaw, } if err := survey.AskOne(prompt, &response); err != nil { return swarm.Service{}, err } chosenService := strings.TrimSpace(strings.Split(response, " ")[0]) for _, service := range matchingServices { serviceName := strings.ToLower(service.Spec.Name) if serviceName == chosenService { return service, nil } } log.Fatal("failed to match chosen service") } return matchingServices[0], nil } // GetService retrieves a service container. If prompt is true and the retrievd // count of service containers does not match 1, then a prompt is presented to // let the user choose. A count of 0 is handled gracefully. func GetService(c context.Context, cl *client.Client, filters filters.Args, prompt bool) (swarm.Service, error) { serviceOpts := types.ServiceListOptions{Filters: filters} services, err := cl.ServiceList(c, serviceOpts) if err != nil { return swarm.Service{}, err } if len(services) == 0 { filter := filters.Get("name")[0] return swarm.Service{}, fmt.Errorf("no services matching the %v filter found?", filter) } if len(services) != 1 { var servicesRaw []string for _, service := range services { serviceName := service.Spec.Name created := formatter.HumanDuration(service.CreatedAt.Unix()) servicesRaw = append(servicesRaw, fmt.Sprintf("%s (created %v)", serviceName, created)) } if !prompt { err := fmt.Errorf("expected 1 service but found %v: %s", len(services), strings.Join(servicesRaw, " ")) return swarm.Service{}, err } log.Warnf("ambiguous service list received, prompting for input") var response string prompt := &survey.Select{ Message: "which service are you looking for?", Options: servicesRaw, } if err := survey.AskOne(prompt, &response); err != nil { return swarm.Service{}, err } chosenService := strings.TrimSpace(strings.Split(response, " ")[0]) for _, service := range services { serviceName := strings.ToLower(service.Spec.Name) if serviceName == chosenService { return service, nil } } log.Fatal("failed to match chosen service") } return services[0], nil } // ContainerToServiceName converts a container name to a service name. func ContainerToServiceName(containerNames []string, stackName string) string { containerName := strings.Join(containerNames, "") trimmed := strings.TrimPrefix(containerName, "/") stackNameServiceName := strings.Split(trimmed, ".")[0] splitter := fmt.Sprintf("%s_", stackName) return strings.Split(stackNameServiceName, splitter)[1] }