in plugin/commander/container/docker/docker.go [49:152]
func findTargetContainer(dockerClient *dockerclient.Client, containerId, containerName string) (*types.Container, error) {
// Three situations MUST be carefully handled:
// 1. Only container id specified fortunately.
// 2. Only container name specified, which should be validated to be conform
// to the requirement in API reference.
// 3. Both specified. Not only the format of container name should be
// validated at first, but the name itself needs to be checked if it belongs
// to the container.
if containerName != "" {
if !containerNameValidator.MatchString(containerName) {
return nil, taskerrors.NewInvalidContainerNameError()
}
}
containerListFilterKVs := make([]filters.KeyValuePair, 0, 2)
// Although the original ContainerId parameter supports Kubernetes's special
// <runtime>://<container-id> format, but don't worry here. runtime prefix
// has been correctly stripped in caller function.
if containerId != "" {
containerListFilterKVs = append(containerListFilterKVs, filters.KeyValuePair{
Key: "id",
Value: containerId,
})
} else if containerName != "" {
containerListFilterKVs = append(containerListFilterKVs, filters.KeyValuePair{
Key: "name",
Value: containerName,
})
}
containerListOptions := types.ContainerListOptions{
All: true,
Filters: filters.NewArgs(containerListFilterKVs...),
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
foundContainers, err := dockerClient.ContainerList(ctx, containerListOptions)
if ctxErr := ctx.Err(); ctxErr != nil {
log.GetLogger().WithError(err).Warningf("Failed to list docker containers matching filter %v", containerListFilterKVs)
if err != nil {
return nil, taskerrors.NewContainerRuntimeTimeoutError(err)
} else {
return nil, taskerrors.NewContainerRuntimeTimeoutError(ctxErr)
}
}
if err != nil {
log.GetLogger().WithError(err).Warningf("Failed to list docker containers matching filter %v", containerListFilterKVs)
return nil, taskerrors.NewContainerRuntimeInternalError(err)
}
if len(foundContainers) == 0 {
return nil, taskerrors.NewContainerNotFoundError()
}
if containerId != "" && containerName != "" {
// Container names in output of `docker ps`, `docker container list` or
// `docker list` are not prefixed by a slash '/'. HOWEVER container
// names got from docker's client.ContainerList() does have it, i.e.,
// what you see is NOT what you get. Looking inside the source of docker
// CLI, magics are applied to these names before presented to Muggles,
// and that's what we need in our humble pre-processing phrase before
// naive container filtering by name.
// See link below for detail implementation in docker CLI:
// https://github.com/docker/cli/blob/67cc8b1fd88aea06690eaf3e5d56acd68a0178d2/cli/command/formatter/container.go#L125-L148
//
// And I know, I know passing both container id and name to Docker's
// ContainerList API would be a simpler solution. But it is a little
// expensive than filtering by ourselves. Consideration is still open.
prefixedContainerName := containerName
if prefixedContainerName[0] != '/' {
prefixedContainerName = "/" + prefixedContainerName
}
filteredContainers := make([]types.Container, 0, len(foundContainers))
for _, container := range foundContainers {
for _, name := range container.Names {
if name == prefixedContainerName {
filteredContainers = append(filteredContainers, container)
break
}
}
}
if len(filteredContainers) == 0 {
return nil, taskerrors.NewContainerNameAndIdNotMatchError(containerId, containerName)
} else {
foundContainers = filteredContainers
}
}
if len(foundContainers) == 0 {
return nil, taskerrors.NewContainerNotFoundError()
}
if len(foundContainers) > 1 {
return nil, taskerrors.NewContainerNameDuplicatedError()
}
// TODO: FIXME: Use uniform container state representation and convert to it
canonicalizedContainerState := strings.ToUpper(foundContainers[0].State)
if canonicalizedContainerState != "RUNNING" {
return nil, taskerrors.NewContainerStateAbnormalError(canonicalizedContainerState)
}
return &foundContainers[0], nil
}