github.com/docker/libcompose@v0.4.1-0.20210616120443-2a046c0bdbf2/project/context.go (about)

     1  package project
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path"
     8  	"path/filepath"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/docker/libcompose/config"
    13  	"github.com/docker/libcompose/logger"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  var projectRegexp = regexp.MustCompile("[^a-zA-Z0-9_.-]")
    18  
    19  // Context holds context meta information about a libcompose project, like
    20  // the project name, the compose file, etc.
    21  type Context struct {
    22  	ComposeFiles        []string
    23  	ComposeBytes        [][]byte
    24  	ProjectName         string
    25  	isOpen              bool
    26  	ServiceFactory      ServiceFactory
    27  	NetworksFactory     NetworksFactory
    28  	VolumesFactory      VolumesFactory
    29  	EnvironmentLookup   config.EnvironmentLookup
    30  	ResourceLookup      config.ResourceLookup
    31  	LoggerFactory       logger.Factory
    32  	IgnoreMissingConfig bool
    33  	Project             *Project
    34  }
    35  
    36  func (c *Context) readComposeFiles() error {
    37  	if c.ComposeBytes != nil {
    38  		return nil
    39  	}
    40  
    41  	logrus.Debugf("Opening compose files: %s", strings.Join(c.ComposeFiles, ","))
    42  
    43  	// Handle STDIN (`-f -`)
    44  	if len(c.ComposeFiles) == 1 && c.ComposeFiles[0] == "-" {
    45  		composeBytes, err := ioutil.ReadAll(os.Stdin)
    46  		if err != nil {
    47  			logrus.Errorf("Failed to read compose file from stdin: %v", err)
    48  			return err
    49  		}
    50  		c.ComposeBytes = [][]byte{composeBytes}
    51  		return nil
    52  	}
    53  
    54  	for _, composeFile := range c.ComposeFiles {
    55  		composeBytes, err := ioutil.ReadFile(composeFile)
    56  		if err != nil && !os.IsNotExist(err) {
    57  			logrus.Errorf("Failed to open the compose file: %s", composeFile)
    58  			return err
    59  		}
    60  		if err != nil && !c.IgnoreMissingConfig {
    61  			logrus.Errorf("Failed to find the compose file: %s", composeFile)
    62  			return err
    63  		}
    64  		c.ComposeBytes = append(c.ComposeBytes, composeBytes)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (c *Context) determineProject() error {
    71  	name, err := c.lookupProjectName()
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	c.ProjectName = normalizeName(name)
    77  
    78  	if c.ProjectName == "" {
    79  		return fmt.Errorf("Falied to determine project name")
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  func (c *Context) lookupProjectName() (string, error) {
    86  	if c.ProjectName != "" {
    87  		return c.ProjectName, nil
    88  	}
    89  
    90  	if envProject := os.Getenv("COMPOSE_PROJECT_NAME"); envProject != "" {
    91  		return envProject, nil
    92  	}
    93  
    94  	file := "."
    95  	if len(c.ComposeFiles) > 0 {
    96  		file = c.ComposeFiles[0]
    97  	}
    98  
    99  	f, err := filepath.Abs(file)
   100  	if err != nil {
   101  		logrus.Errorf("Failed to get absolute directory for: %s", file)
   102  		return "", err
   103  	}
   104  
   105  	f = toUnixPath(f)
   106  
   107  	parent := path.Base(path.Dir(f))
   108  	if parent != "" && parent != "." {
   109  		return parent, nil
   110  	} else if wd, err := os.Getwd(); err != nil {
   111  		return "", err
   112  	} else {
   113  		return path.Base(toUnixPath(wd)), nil
   114  	}
   115  }
   116  
   117  func normalizeName(name string) string {
   118  	r := regexp.MustCompile("[^a-z0-9]+")
   119  	return r.ReplaceAllString(strings.ToLower(name), "")
   120  }
   121  
   122  func toUnixPath(p string) string {
   123  	return strings.Replace(p, "\\", "/", -1)
   124  }
   125  
   126  func (c *Context) open() error {
   127  	if c.isOpen {
   128  		return nil
   129  	}
   130  
   131  	if err := c.readComposeFiles(); err != nil {
   132  		return err
   133  	}
   134  
   135  	if err := c.determineProject(); err != nil {
   136  		return err
   137  	}
   138  
   139  	c.isOpen = true
   140  	return nil
   141  }