bifrost/convert.go
package bifrost
import (
"fmt"
"code.cloudfoundry.org/eirini"
"code.cloudfoundry.org/eirini/api"
"code.cloudfoundry.org/eirini/models/cf"
"code.cloudfoundry.org/eirini/util"
"code.cloudfoundry.org/lager"
"github.com/pkg/errors"
)
type lifecycleOptions struct {
command []string
env map[string]string
image string
privateRegistry *api.PrivateRegistry
}
type APIConverter struct {
logger lager.Logger
}
func NewAPIConverter(logger lager.Logger) *APIConverter {
return &APIConverter{
logger: logger,
}
}
func (c *APIConverter) ConvertLRP(request cf.DesireLRPRequest) (api.LRP, error) {
env := map[string]string{
"LANG": "en_US.UTF-8",
}
var port int32
if len(request.Ports) != 0 {
port = request.Ports[0]
env[eirini.EnvCFInstanceAddr] = fmt.Sprintf("0.0.0.0:%d", port)
env[eirini.EnvCFInstancePort] = fmt.Sprintf("%d", port)
env[eirini.EnvCFInstancePorts] = fmt.Sprintf(`[{"external":%d,"internal":%d}]`, port, port)
}
healthcheck := api.Healthcheck{
Type: request.HealthCheckType,
Endpoint: request.HealthCheckHTTPEndpoint,
TimeoutMs: request.HealthCheckTimeoutMs,
Port: port,
}
lrpLifecycleOptions, err := c.getLifecycleOptions(request)
if err != nil {
return api.LRP{}, err
}
identifier := api.LRPIdentifier{
GUID: request.GUID,
Version: request.Version,
}
err = c.validateRequest(request)
if err != nil {
return api.LRP{}, err
}
return api.LRP{
AppName: request.AppName,
AppGUID: request.AppGUID,
LastUpdated: request.LastUpdated,
OrgName: request.OrganizationName,
OrgGUID: request.OrganizationGUID,
SpaceName: request.SpaceName,
SpaceGUID: request.SpaceGUID,
LRPIdentifier: identifier,
ProcessType: request.ProcessType,
Image: lrpLifecycleOptions.image,
TargetInstances: request.NumInstances,
Command: lrpLifecycleOptions.command,
Env: mergeMaps(request.Environment, env, lrpLifecycleOptions.env),
Health: healthcheck,
Ports: request.Ports,
MemoryMB: request.MemoryMB,
DiskMB: request.DiskMB,
CPUWeight: request.CPUWeight,
VolumeMounts: convertVolumeMounts(request),
LRP: request.LRP,
UserDefinedAnnotations: request.UserDefinedAnnotations,
PrivateRegistry: lrpLifecycleOptions.privateRegistry,
}, nil
}
func (c *APIConverter) ConvertTask(taskGUID string, request cf.TaskRequest) (api.Task, error) {
c.logger.Debug("convert-task", lager.Data{"app-id": request.AppGUID, "task-guid": taskGUID})
env := map[string]string{
"HOME": "/home/vcap/app",
"PATH": "/usr/local/bin:/usr/bin:/bin",
"USER": "vcap",
"TMPDIR": "/home/vcap/tmp",
}
task := api.Task{
GUID: taskGUID,
Name: request.Name,
CompletionCallback: request.CompletionCallback,
AppName: request.AppName,
AppGUID: request.AppGUID,
OrgName: request.OrgName,
SpaceName: request.SpaceName,
OrgGUID: request.OrgGUID,
SpaceGUID: request.SpaceGUID,
}
if request.Lifecycle.DockerLifecycle == nil {
return api.Task{}, errors.New("docker is the only supported lifecycle")
}
lifecycle := request.Lifecycle.DockerLifecycle
task.Command = lifecycle.Command
task.Image = lifecycle.Image
if lifecycle.RegistryUsername != "" || lifecycle.RegistryPassword != "" {
task.PrivateRegistry = &api.PrivateRegistry{
Server: util.ParseImageRegistryHost(lifecycle.Image),
Username: lifecycle.RegistryUsername,
Password: lifecycle.RegistryPassword,
}
}
task.Env = mergeEnvs(request.Environment, env)
return task, nil
}
func mergeMaps(maps ...map[string]string) map[string]string {
result := make(map[string]string)
for _, m := range maps {
for k, v := range m {
result[k] = v
}
}
return result
}
func mergeEnvs(requestEnv []cf.EnvironmentVariable, appliedEnv map[string]string) map[string]string {
result := make(map[string]string)
for _, v := range requestEnv {
result[v.Name] = v.Value
}
for k, v := range appliedEnv {
result[k] = v
}
return result
}
func (c *APIConverter) getLifecycleOptions(request cf.DesireLRPRequest) (*lifecycleOptions, error) {
options := &lifecycleOptions{}
if request.Lifecycle.DockerLifecycle == nil {
return nil, fmt.Errorf("missing lifecycle data")
}
var err error
lifecycle := request.Lifecycle.DockerLifecycle
options.image = lifecycle.Image
options.command = lifecycle.Command
if err != nil {
return nil, errors.Wrap(err, "failed to verify if docker image needs root user")
}
registryUsername := lifecycle.RegistryUsername
registryPassword := lifecycle.RegistryPassword
if registryUsername != "" || registryPassword != "" {
options.privateRegistry = &api.PrivateRegistry{
Server: util.ParseImageRegistryHost(options.image),
Username: registryUsername,
Password: registryPassword,
}
}
return options, nil
}
func convertVolumeMounts(request cf.DesireLRPRequest) []api.VolumeMount {
volumeMounts := []api.VolumeMount{}
for _, vm := range request.VolumeMounts {
volumeMounts = append(volumeMounts, api.VolumeMount{
MountPath: vm.MountDir,
ClaimName: vm.VolumeID,
})
}
return volumeMounts
}
func (c *APIConverter) validateRequest(request cf.DesireLRPRequest) error {
if request.DiskMB == 0 {
return errors.New("DiskMB cannot be 0")
}
return nil
}