Beispiel #1
0
 def __init__(self,
              user,
              gitReadHash="master",
              ignoreVersionLocks=False,
              initialized=False):
     self.__subusers = None
     self.__changeLog = u""
     self.commit_message = None
     self.__changed = False
     self.logOutputVerbosity = 2
     self.initialized = initialized
     self.lastVerbosityLevel = None
     self.ignoreVersionLocks = ignoreVersionLocks
     if "SUBUSER_VERBOSITY" in os.environ:
         try:
             self.logOutputVerbosity = int(os.environ["SUBUSER_VERBOSITY"])
         except ValueError:
             subuserlib.print.printWithoutCrashing(
                 "Invalid verbosity setting! Verbosity may be set to any integer."
             )
     self.__repositories = None
     self.gitRepository = None
     self.gitReadHash = gitReadHash
     userOwnedObject.UserOwnedObject.__init__(self, user)
     self.registryDir = self.user.config["registry-dir"]
     self.logFilePath = os.path.join(self.registryDir, "commit_log")
     self.gitRepository = GitRepository(self.user, self.registryDir)
Beispiel #2
0
def getSubuserVersion():
    if subuserlib.test.testing:
        return "0.5"
    with open(subuserlib.paths.getSubuserDataFile("VERSION")) as f:
        stableVersion = f.read().strip()
    if os.path.exists(os.path.join(subuserlib.paths.getSubuserDir(), ".git")):
        gitRepo = GitRepository(subuserlib.paths.getSubuserDir())
        gitHash = gitRepo.getHashOfRef("HEAD")
        return stableVersion + "-dev-" + gitHash
    else:
        return stableVersion
def getSubuserVersion():
  if subuserlib.test.testing:
    return "0.5"
  with open(subuserlib.paths.getSubuserDataFile("VERSION")) as f:
    stableVersion = f.read().strip()
  if os.path.exists(os.path.join(subuserlib.paths.getSubuserDir(),".git")):
    gitRepo = GitRepository(subuserlib.paths.getSubuserDir())
    gitHash = gitRepo.getHashOfRef("HEAD")
    return stableVersion+"-dev-"+gitHash
  else:
    return stableVersion
Beispiel #4
0
def getSubuserVersion(user):
    if subuserlib.test.testing:
        return "0.5"
    with open(subuserlib.paths.getSubuserDataFile("VERSION")) as f:
        stableVersion = f.read().strip()
    if os.path.exists(os.path.join(subuserlib.paths.getSubuserDir(), ".git")):
        gitRepo = GitRepository(user, subuserlib.paths.getSubuserDir())
        gitHash = gitRepo.getHashOfRef("HEAD")
        devVersionString = stableVersion + "-dev-" + gitHash
        if gitRepo.doesHaveUncommittedChanges("HEAD"):
            devVersionString += "-dirty"
        return devVersionString
    else:
        return stableVersion
Beispiel #5
0
def getSubuserVersion(user):
  if subuserlib.test.testing:
    return "0.5"
  with open(subuserlib.paths.getSubuserDataFile("VERSION")) as f:
    stableVersion = f.read().strip()
  if os.path.exists(os.path.join(subuserlib.paths.getSubuserDir(),".git")):
    gitRepo = GitRepository(user,subuserlib.paths.getSubuserDir())
    gitHash = gitRepo.getHashOfRef("HEAD")
    devVersionString = stableVersion+"-dev-"+gitHash
    if gitRepo.doesHaveUncommittedChanges("HEAD"):
      devVersionString += "-dirty"
    return devVersionString
  else:
    return stableVersion
Beispiel #6
0
 def __init__(self,
              user,
              gitReadHash="master",
              ignoreVersionLocks=False,
              initialized=False):
     self.__subusers = None
     self.__changeLog = u""
     self.__changed = False
     self.__logOutputVerbosity = 1
     self.initialized = initialized
     self.ignoreVersionLocks = ignoreVersionLocks
     if "SUBUSER_VERBOSITY" in os.environ:
         try:
             self.__logOutputVerbosity = int(
                 os.environ["SUBUSER_VERBOSITY"])
         except ValueError:
             subuserlib.print.printWithoutCrashing(
                 "Invalid verbosity setting! Verbosity may be set to any integer."
             )
     self.__repositories = None
     self.__gitRepository = None
     self.__gitReadHash = gitReadHash
     userOwnedObject.UserOwnedObject.__init__(self, user)
     self.__gitRepository = GitRepository(
         self.getUser(),
         self.getUser().getConfig()["registry-dir"])
Beispiel #7
0
 def __init__(self,user,gitReadHash="master"):
   self.__subusers = None
   self.__changeLog = ""
   self.__changed = False
   self.__logOutputVerbosity = 2
   self.__repositories = None
   self.__gitRepository = None
   self.__gitReadHash = gitReadHash
   userOwnedObject.UserOwnedObject.__init__(self,user)
   self.__gitRepository = GitRepository(self.getUser().getConfig()["registry-dir"])
   self._ensureGitRepoInitialized()
Beispiel #8
0
 def __init__(self,
              user,
              name,
              gitOriginURI=None,
              gitCommitHash=None,
              temporary=False,
              sourceDir=None):
     """
 Repositories can either be managed by git, or simply be normal directories on the user's computer. If ``sourceDir`` is not set to None, then ``gitOriginURI`` is ignored and the repository is assumed to be a simple directory.
 """
     self.name = name
     self.gitOriginURI = gitOriginURI
     self.gitCommitHash = gitCommitHash
     self.temporary = temporary
     self.sourceDir = sourceDir
     self.__fileStructure = None
     UserOwnedObject.__init__(self, user)
     super().__init__()
     self.gitRepository = GitRepository(user, self.repoPath)
     if not self.isPresent():
         self.updateSources(initialUpdate=True)
     if self.isPresent():
         self.repoConfig = self.loadRepoConfig()
         self.loadImageSources()
Beispiel #9
0
 def __init__(self,user,name,gitOriginURI=None,gitCommitHash=None,temporary=False,sourceDir=None):
   """
   Repositories can either be managed by git, or simply be normal directories on the user's computer. If ``sourceDir`` is not set to None, then ``gitOriginURI`` is ignored and the repository is assumed to be a simple directory.
   """
   self.name = name
   self.gitOriginURI = gitOriginURI
   self.gitCommitHash = gitCommitHash
   self.temporary = temporary
   self.sourceDir = sourceDir
   self.__fileStructure = None
   UserOwnedObject.__init__(self,user)
   super().__init__()
   self.gitRepository = GitRepository(user,self.repoPath)
   if not self.isPresent():
     self.updateSources(initialUpdate=True)
   if self.isPresent():
     self.repoConfig = self.loadRepoConfig()
     self.loadImageSources()
Beispiel #10
0
 def __init__(self,
              user,
              name,
              gitOriginURI=None,
              gitCommitHash=None,
              temporary=False,
              sourceDir=None):
     """
 Repositories can either be managed by git, or simply be normal directories on the user's computer. If ``sourceDir`` is not set to None, then ``gitOriginURI`` is ignored and the repository is assumed to be a simple directory.
 """
     self.__name = name
     self.__gitOriginURI = gitOriginURI
     self.__lastGitCommitHash = gitCommitHash
     self.__temporary = temporary
     self.__sourceDir = sourceDir
     UserOwnedObject.__init__(self, user)
     self.__gitRepository = GitRepository(self.getRepoPath())
     self.loadProgramSources()
Beispiel #11
0
 def __init__(self,user,gitReadHash="master", ignoreVersionLocks=False, initialized = False):
   self.__subusers = None
   self.__changeLog = u""
   self.commit_message = None
   self.__changed = False
   self.logOutputVerbosity = 2
   self.initialized = initialized
   self.lastVerbosityLevel = None
   self.ignoreVersionLocks = ignoreVersionLocks
   if "SUBUSER_VERBOSITY" in os.environ:
     try:
       self.logOutputVerbosity = int(os.environ["SUBUSER_VERBOSITY"])
     except ValueError:
       subuserlib.print.printWithoutCrashing("Invalid verbosity setting! Verbosity may be set to any integer.")
   self.__repositories = None
   self.gitRepository = None
   self.gitReadHash = gitReadHash
   userOwnedObject.UserOwnedObject.__init__(self,user)
   self.registryDir = self.user.config["registry-dir"]
   self.logFilePath = os.path.join(self.registryDir,"commit_log")
   self.gitRepository = GitRepository(self.user,self.registryDir)
Beispiel #12
0
class Registry(userOwnedObject.UserOwnedObject):
  def __init__(self,user,gitReadHash="master", ignoreVersionLocks=False, initialized = False):
    self.__subusers = None
    self.__changeLog = u""
    self.commit_message = None
    self.__changed = False
    self.logOutputVerbosity = 2
    self.initialized = initialized
    self.lastVerbosityLevel = None
    self.ignoreVersionLocks = ignoreVersionLocks
    if "SUBUSER_VERBOSITY" in os.environ:
      try:
        self.logOutputVerbosity = int(os.environ["SUBUSER_VERBOSITY"])
      except ValueError:
        subuserlib.print.printWithoutCrashing("Invalid verbosity setting! Verbosity may be set to any integer.")
    self.__repositories = None
    self.gitRepository = None
    self.gitReadHash = gitReadHash
    userOwnedObject.UserOwnedObject.__init__(self,user)
    self.registryDir = self.user.config["registry-dir"]
    self.logFilePath = os.path.join(self.registryDir,"commit_log")
    self.gitRepository = GitRepository(self.user,self.registryDir)

  @property
  def subusers(self):
    if self.__subusers is None:
      self.__subusers = subusers.Subusers(self.user)
    return self.__subusers

  @property
  def repositories(self):
    if not self.__repositories:
      self.__repositories = repositories.Repositories(self.user)
    return self.__repositories

  def ensureGitRepoInitialized(self):
    if not os.path.exists(os.path.join(self.user.config["registry-dir"],".git")):
      self.initialized = False
      # Ensure git is setup before we start to make changes.
      self.gitRepository.assertGitSetup()
      self.user.endUser.makedirs(self.user.config["registry-dir"])
      try:
        self.gitRepository.run(["init"])
      except subuserlib.classes.gitRepository.GitException as e:
        # Remove partially initialized repository when 'init' command failed
        shutil.rmtree(self.gitRepository.path)
        sys.exit("Git failed with: '%s'." % str(e).strip())
      self.logChange("Initial commit.")
      self.commit("Initial commit.",_no_lock_needed = True)
    self.initialized = True

  def log(self,message,verbosityLevel=1,notify=False):
    """
    If the current verbosity level is equal to or greater than verbosityLevel, print the message to the screen.
    If the current verbosity level is equal to or greater than verbosityLevel minus one, add the message to the log.
    Do not mark the registry as changed.
    The notify option will create a popup dialog with the message if the notify-send command exists.
    """
    message = message.rstrip()
    if (verbosityLevel-1) <= self.logOutputVerbosity:
      self.__changeLog = self.__changeLog + message + u"\n"
    if self.lastVerbosityLevel == 2 and self.logOutputVerbosity == 2 and sys.stdout.isatty():
      CURSOR_UP_ONE = '\x1b[1A'
      ERASE_LINE = '\x1b[2K'
      print(CURSOR_UP_ONE + ERASE_LINE + CURSOR_UP_ONE)
    if verbosityLevel <= self.logOutputVerbosity:
      subuserlib.print.printWithoutCrashing(message)
      if notify and subuserlib.executablePath.which("notify-send"):
        self.user.endUser.call(["notify-send",message])
    self.lastVerbosityLevel = verbosityLevel

  def logChange(self,message,verbosityLevel=1):
    """
    Add a log message to the registry's change log, and mark the registry as changed.
    """
    self.log(message,verbosityLevel=verbosityLevel)
    self.__changed = True

  def setChanged(self,changed=True):
    self.__changed = changed

  def logRenameCommit(self, message):
    """
    Add a new message to the top of the log.
    """
    self.__changeLog = message + u"\n" + self.__changeLog

  def commit(self,message=None,_no_lock_needed=False):
    """
    Git commit the changes to the registry files, installed-miages.json and subusers.json.
    """
    if (not self.user._has_lock) and (not _no_lock_needed):
      sys.exit("Programmer error. Committing to registry without first aquiring lock! Please report this incident to: https://github.com/subuser-security/subuser/issues")
    if self.__changed:
      self.repositories.save()
      self.subusers.save()
      with self.user.endUser.get_file(self.logFilePath) as fd:
        fd.write(self.__changeLog)
      self.gitRepository.run(["add","."])
      if message is None:
        if self.commit_message is not None:
          message = self.commit_message
        else:
          message = self.__changeLog
      self.gitRepository.commit(message)
      # Log to live log
      announcement = {}
      announcement["commit"] = self.gitRepository.getHashOfRef("master")
      self.logToLiveLog(announcement)
      self.__changed = False
      self.__changeLog = u""

  def logToLiveLog(self,announcement):
    announcementJson = json.dumps(announcement)
    liveLogDir=os.path.join(self.user.endUser.homeDir,".subuser/registry-live-log")
    if os.path.isdir(liveLogDir):
      for liveLogPid in os.listdir(liveLogDir):
        liveLogPath = os.path.join(liveLogDir,liveLogPid)
        try:
          liveLog = os.open(liveLogPath,os.O_WRONLY|os.O_NONBLOCK)
          os.write(liveLog,announcementJson)
        except OSError:
          pass
        # TODO Note: We don't close the file descriptors, because doing so makes the pipe close on the other end too. This would be a file descriptor leak if this method was used in any long running process(which it is not).

  def cleanOutOldPermissions(self):
    for permissions_folder_name in self.gitRepository.getFileStructureAtCommit(self.gitReadHash).lsFolders("permissions"):
      exists = os.path.exists(os.path.join(self.registryDir,"permissions",permissions_folder_name))
      if exists and permissions_folder_name not in self.subusers:
        self.logChange("Removing left over permissions for no-longer extant subuser %s"%permissions_folder_name,2)
        try:
          self.gitRepository.run(["rm","-r",os.path.join("permissions",permissions_folder_name)])
        except subuserlib.classes.gitRepository.GitException as e:
          self.log(" %s"%str(e))
Beispiel #13
0
class Registry(userOwnedObject.UserOwnedObject):
    def __init__(self,
                 user,
                 gitReadHash="master",
                 ignoreVersionLocks=False,
                 initialized=False):
        self.__subusers = None
        self.__changeLog = u""
        self.commit_message = None
        self.__changed = False
        self.logOutputVerbosity = 2
        self.initialized = initialized
        self.lastVerbosityLevel = None
        self.ignoreVersionLocks = ignoreVersionLocks
        if "SUBUSER_VERBOSITY" in os.environ:
            try:
                self.logOutputVerbosity = int(os.environ["SUBUSER_VERBOSITY"])
            except ValueError:
                subuserlib.print.printWithoutCrashing(
                    "Invalid verbosity setting! Verbosity may be set to any integer."
                )
        self.__repositories = None
        self.gitRepository = None
        self.gitReadHash = gitReadHash
        userOwnedObject.UserOwnedObject.__init__(self, user)
        self.registryDir = self.user.config["registry-dir"]
        self.logFilePath = os.path.join(self.registryDir, "commit_log")
        self.gitRepository = GitRepository(self.user, self.registryDir)

    @property
    def subusers(self):
        if self.__subusers is None:
            self.__subusers = subusers.Subusers(self.user)
        return self.__subusers

    @property
    def repositories(self):
        if not self.__repositories:
            self.__repositories = repositories.Repositories(self.user)
        return self.__repositories

    def ensureGitRepoInitialized(self):
        if not os.path.exists(
                os.path.join(self.user.config["registry-dir"], ".git")):
            self.initialized = False
            # Ensure git is setup before we start to make changes.
            self.gitRepository.assertGitSetup()
            self.user.endUser.makedirs(self.user.config["registry-dir"])
            try:
                self.gitRepository.run(["init"])
            except subuserlib.classes.gitRepository.GitException as e:
                # Remove partially initialized repository when 'init' command failed
                shutil.rmtree(self.gitRepository.path)
                sys.exit("Git failed with: '%s'." % str(e).strip())
            self.logChange("Initial commit.")
            self.commit("Initial commit.", _no_lock_needed=True)
        self.initialized = True

    def log(self, message, verbosityLevel=1, notify=False):
        """
    If the current verbosity level is equal to or greater than verbosityLevel, print the message to the screen.
    If the current verbosity level is equal to or greater than verbosityLevel minus one, add the message to the log.
    Do not mark the registry as changed.
    The notify option will create a popup dialog with the message if the notify-send command exists.
    """
        message = message.rstrip()
        if (verbosityLevel - 1) <= self.logOutputVerbosity:
            self.__changeLog = self.__changeLog + message + u"\n"
        if self.lastVerbosityLevel == 2 and self.logOutputVerbosity == 2 and sys.stdout.isatty(
        ):
            CURSOR_UP_ONE = '\x1b[1A'
            ERASE_LINE = '\x1b[2K'
            print(CURSOR_UP_ONE + ERASE_LINE + CURSOR_UP_ONE)
        if verbosityLevel <= self.logOutputVerbosity:
            subuserlib.print.printWithoutCrashing(message)
            if notify and subuserlib.executablePath.which("notify-send"):
                self.user.endUser.call(["notify-send", message])
        self.lastVerbosityLevel = verbosityLevel

    def logChange(self, message, verbosityLevel=1):
        """
    Add a log message to the registry's change log, and mark the registry as changed.
    """
        self.log(message, verbosityLevel=verbosityLevel)
        self.__changed = True

    def setChanged(self, changed=True):
        self.__changed = changed

    def logRenameCommit(self, message):
        """
    Add a new message to the top of the log.
    """
        self.__changeLog = message + u"\n" + self.__changeLog

    def commit(self, message=None, _no_lock_needed=False):
        """
    Git commit the changes to the registry files, installed-miages.json and subusers.json.
    """
        if (not self.user._has_lock) and (not _no_lock_needed):
            sys.exit(
                "Programmer error. Committing to registry without first aquiring lock! Please report this incident to: https://github.com/subuser-security/subuser/issues"
            )
        if self.__changed:
            self.repositories.save()
            self.subusers.save()
            with self.user.endUser.get_file(self.logFilePath) as fd:
                fd.write(self.__changeLog)
            self.gitRepository.run(["add", "."])
            if message is None:
                if self.commit_message is not None:
                    message = self.commit_message
                else:
                    message = self.__changeLog
            self.gitRepository.commit(message)
            # Log to live log
            announcement = {}
            announcement["commit"] = self.gitRepository.getHashOfRef("master")
            self.logToLiveLog(announcement)
            self.__changed = False
            self.__changeLog = u""

    def logToLiveLog(self, announcement):
        announcementJson = json.dumps(announcement)
        liveLogDir = os.path.join(self.user.endUser.homeDir,
                                  ".subuser/registry-live-log")
        if os.path.isdir(liveLogDir):
            for liveLogPid in os.listdir(liveLogDir):
                liveLogPath = os.path.join(liveLogDir, liveLogPid)
                try:
                    liveLog = os.open(liveLogPath, os.O_WRONLY | os.O_NONBLOCK)
                    os.write(liveLog, announcementJson)
                except OSError:
                    pass
                # TODO Note: We don't close the file descriptors, because doing so makes the pipe close on the other end too. This would be a file descriptor leak if this method was used in any long running process(which it is not).

    def cleanOutOldPermissions(self):
        for permissions_folder_name in self.gitRepository.getFileStructureAtCommit(
                self.gitReadHash).lsFolders("permissions"):
            exists = os.path.exists(
                os.path.join(self.registryDir, "permissions",
                             permissions_folder_name))
            if exists and permissions_folder_name not in self.subusers:
                self.logChange(
                    "Removing left over permissions for no-longer extant subuser %s"
                    % permissions_folder_name, 2)
                try:
                    self.gitRepository.run([
                        "rm", "-r",
                        os.path.join("permissions", permissions_folder_name)
                    ])
                except subuserlib.classes.gitRepository.GitException as e:
                    self.log(" %s" % str(e))
Beispiel #14
0
class Repository(OrderedDict,UserOwnedObject,Describable):
  def __init__(self,user,name,gitOriginURI=None,gitCommitHash=None,temporary=False,sourceDir=None):
    """
    Repositories can either be managed by git, or simply be normal directories on the user's computer. If ``sourceDir`` is not set to None, then ``gitOriginURI`` is ignored and the repository is assumed to be a simple directory.
    """
    self.name = name
    self.gitOriginURI = gitOriginURI
    self.gitCommitHash = gitCommitHash
    self.temporary = temporary
    self.sourceDir = sourceDir
    self.__fileStructure = None
    UserOwnedObject.__init__(self,user)
    super().__init__()
    self.gitRepository = GitRepository(user,self.repoPath)
    if not self.isPresent():
      self.updateSources(initialUpdate=True)
    if self.isPresent():
      self.repoConfig = self.loadRepoConfig()
      self.loadImageSources()

  @property
  def uri(self):
    if self.isLocal:
      return self.sourceDir
    else:
      return self.gitOriginURI

  @property
  def fileStructure(self):
    if self.__fileStructure is None:
      if self.isLocal:
        self.__fileStructure = BasicFileStructure(self.sourceDir)
      else:
        if self.gitCommitHash is None:
          self.updateGitCommitHash()
        self.__fileStructure = self.gitRepository.getFileStructureAtCommit(self.gitCommitHash)
    return self.__fileStructure

  @property
  def displayName(self):
    """
    How should we refer to this repository when communicating with the user?
    """
    if self.temporary:
      if self.isLocal:
        return self.sourceDir
      else:
        return self.gitOriginURI
    else:
      return self.name

  def describe(self):
    print("Repository: "+self.displayName)
    print("------------")
    if self.isLocal:
      print("Is a local(non-git) repository.")
      if not self.temporary:
        print("Located at: " + self.repoPath)
    if self.temporary:
      print("Is a temporary repository.")
    else:
      if not self.isLocal:
        print("Cloned from: "+self.gitOriginURI)
        print("Currently at commit: "+self.gitCommitHash)

  def getSortedList(self):
    """
    Return a list of image sources sorted by name.
    """
    return list(sorted(self.values(),key=lambda imageSource:imageSource.name))

  @property
  def repoPath(self):
    """ Get the path of the repo's sources on disk. """
    if self.isLocal:
      return self.sourceDir
    else:
      return os.path.join(self.user.config["repositories-dir"],self.name)

  def loadRepoConfig(self):
    """
    Either returns the config as a dictionary or None if no configuration exists or can be parsed.
    """
    def verifyPaths(dictionary,paths):
      """
      Looks through a dictionary at all entries that can be considered to be paths, and ensures that they do not contain any relative upwards references.
      Throws a ValueError if they do.
      """
      for path in paths:
        if path in dictionary and path.startswith("../") or "/../" in path:
          raise ValueError("Paths in .subuser.json may not be relative to a higher directory.")
    if self.fileStructure.exists("./.subuser.json"):
      configFileContents = self.fileStructure.read("./.subuser.json")
    else:
      return None
    repoConfig = json.loads(configFileContents)
    # Validate untrusted input
    verifyPaths(repoConfig,["image-sources-dir"])
    if "explicit-image-sources" in repoConfig:
      for explicitImageSource in repoConfig["explicit-image-sources"]:
        verifyPaths(explicitImageSource,["image-file","permissions-file","build-context"])
    return repoConfig

  @property
  def imageSourcesDir(self):
    """
    Get the path of the repo's subuser root on disk on the host.
    """
    return os.path.join(self.repoPath,self.relativeImageSourcesDir)

  @property
  def relativeImageSourcesDir(self):
    """
    Get the path of the repo's subuser root on disk on the host.
    """
    repoConfig = self.repoConfig
    if repoConfig and "image-sources-dir" in repoConfig:
      return repoConfig["image-sources-dir"]
    else:
      return "./"

  def isInUse(self):
    """
    Are there any installed images or subusers from this repository?
    """
    for _,installedImage in self.user.installedImages.items():
      if self.name == installedImage.sourceRepoId:
          return True
      for _,subuser in self.user.registry.subusers.items():
        try:
          if self.name == subuser.imageSource.repo.name:
            return True
        except subuserlib.classes.subuser.NoImageSourceException:
          pass
    return False

  @property
  def isLocal(self):
    if self.sourceDir:
      return True
    return False

  def removeGitRepo(self):
    """
    Remove the downloaded git repo associated with this repository from disk.
    """
    if not self.isLocal:
      shutil.rmtree(self.repoPath)

  def isPresent(self):
    """
    Returns True if the repository's files are present on the system. (Cloned or local)
    """
    return os.path.exists(self.repoPath)

  def updateSources(self,initialUpdate=False):
    """
    Pull(or clone) the repo's ImageSources from git origin.
    """
    if self.isLocal:
      return
    if not self.isPresent():
      new = True
      self.user.registry.log("Cloning repository "+self.name+" from "+self.gitOriginURI)
      if self.gitRepository.clone(self.gitOriginURI) != 0:
        self.user.registry.log("Clone failed.")
        return
    else:
      new = False
    try:
      self.gitRepository.run(["fetch","--all"])
    except Exception: # For some reason, git outputs normal messages to stderr.
      pass
    if self.updateGitCommitHash():
      if not new:
        self.user.registry.logChange("Updated repository "+self.displayName)
      if not initialUpdate:
        self.loadImageSources()

  def serializeToDict(self):
    """
    Return a dictionary which describes the image sources available in this repository.
    """
    imageSourcesDict = {}
    for name,imageSource in self.items():
      imageSourcesDict[name] = imageSource.serializeToDict()
    return imageSourcesDict

  def loadImageSources(self):
    """
    Load ImageSources from disk into memory.
    """
    imageNames = self.fileStructure.lsFolders(self.relativeImageSourcesDir)
    for imageName in imageNames:
      imageSource = ImageSource(self.user,self,imageName)
      if self.fileStructure.exists(imageSource.getRelativePermissionsFilePath()):
        self[imageName] = imageSource
    if self.repoConfig is not None and "explicit-image-sources" in self.repoConfig:
      for imageName,config in self.repoConfig["explicit-image-sources"].items():
        assert config is not None
        self[imageName] = ImageSource(self.user,self,imageName,explicitConfig = config)

  def updateGitCommitHash(self):
    """
    Update the internally stored git commit hash to the current git HEAD of the repository.
    Returns True if the repository has been updated.
    Otherwise false.
    """
    if self.isLocal:
      return True
    # Default
    master = "refs/remotes/origin/master"
    newCommitHash = self.gitRepository.getHashOfRef(master)
    # First we check for version constraints on the repository.
    if self.gitRepository.getFileStructureAtCommit(master).exists("./.subuser.json"):
      configFileContents = self.gitRepository.getFileStructureAtCommit(master).read("./.subuser.json")
      configAtMaster = json.loads(configFileContents)
      if "subuser-version-constraints" in configAtMaster:
        versionConstraints = configAtMaster["subuser-version-constraints"]
        subuserVersion = subuserlib.version.getSubuserVersion(self.user)
        for constraint in versionConstraints:
          if not len(constraint) == 3:
            raise SyntaxError("Error in .subuser.json file. Invalid subuser-version-constraints."+ str(versionConstraints))
          op,version,commit = constraint
          from operator import lt,le,eq,ge,gt
          operators = {"<":lt,"<=":le,"==":eq,">=":ge,">":gt}
          try:
            matched = operators[op](subuserVersion,version)
          except KeyError:
            raise SyntaxError("Error in .subuser.json file. Invalid subuser-version-constraints.  \""+op+"\" is not a valid operator.\n\n"+ str(versionConstraints))
          if matched:
            try:
              newCommitHash = self.gitRepository.getHashOfRef("refs/remotes/origin/"+commit)
            except OSError as e:
              if len(commit) == 40:
                newCommitHash = commit
              else:
                raise e
            break
        else:
          raise SyntaxError("Error reading .subuser.json file, no version constraints matched the current subuser version ("+subuserVersion+").\n\n"+str(versionConstraints))
    updated = not (newCommitHash == self.gitCommitHash)
    self.gitCommitHash = newCommitHash
    self.__fileStructure = None
    return updated
Beispiel #15
0
class Repository(OrderedDict, UserOwnedObject, Describable):
    def __init__(self,
                 user,
                 name,
                 gitOriginURI=None,
                 gitCommitHash=None,
                 temporary=False,
                 sourceDir=None):
        """
    Repositories can either be managed by git, or simply be normal directories on the user's computer. If ``sourceDir`` is not set to None, then ``gitOriginURI`` is ignored and the repository is assumed to be a simple directory.
    """
        self.name = name
        self.gitOriginURI = gitOriginURI
        self.gitCommitHash = gitCommitHash
        self.temporary = temporary
        self.sourceDir = sourceDir
        self.__fileStructure = None
        UserOwnedObject.__init__(self, user)
        super().__init__()
        self.gitRepository = GitRepository(user, self.repoPath)
        if not self.isPresent():
            self.updateSources(initialUpdate=True)
        if self.isPresent():
            self.repoConfig = self.loadRepoConfig()
            self.loadImageSources()

    @property
    def uri(self):
        if self.isLocal:
            return self.sourceDir
        else:
            return self.gitOriginURI

    @property
    def fileStructure(self):
        if self.__fileStructure is None:
            if self.isLocal:
                self.__fileStructure = BasicFileStructure(self.sourceDir)
            else:
                if self.gitCommitHash is None:
                    self.updateGitCommitHash()
                self.__fileStructure = self.gitRepository.getFileStructureAtCommit(
                    self.gitCommitHash)
        return self.__fileStructure

    @property
    def displayName(self):
        """
    How should we refer to this repository when communicating with the user?
    """
        if self.temporary:
            if self.isLocal:
                return self.sourceDir
            else:
                return self.gitOriginURI
        else:
            return self.name

    def describe(self):
        print("Repository: " + self.displayName)
        print("------------")
        if self.isLocal:
            print("Is a local(non-git) repository.")
            if not self.temporary:
                print("Located at: " + self.repoPath)
        if self.temporary:
            print("Is a temporary repository.")
        else:
            if not self.isLocal:
                print("Cloned from: " + self.gitOriginURI)
                print("Currently at commit: " + self.gitCommitHash)

    def getSortedList(self):
        """
    Return a list of image sources sorted by name.
    """
        return list(
            sorted(self.values(), key=lambda imageSource: imageSource.name))

    @property
    def repoPath(self):
        """ Get the path of the repo's sources on disk. """
        if self.isLocal:
            return self.sourceDir
        else:
            return os.path.join(self.user.config["repositories-dir"],
                                self.name)

    def loadRepoConfig(self):
        """
    Either returns the config as a dictionary or None if no configuration exists or can be parsed.
    """
        def verifyPaths(dictionary, paths):
            """
      Looks through a dictionary at all entries that can be considered to be paths, and ensures that they do not contain any relative upwards references.
      Throws a ValueError if they do.
      """
            for path in paths:
                if path in dictionary and path.startswith(
                        "../") or "/../" in path:
                    raise ValueError(
                        "Paths in .subuser.json may not be relative to a higher directory."
                    )

        if self.fileStructure.exists("./.subuser.json"):
            configFileContents = self.fileStructure.read("./.subuser.json")
        else:
            return None
        repoConfig = json.loads(configFileContents)
        # Validate untrusted input
        verifyPaths(repoConfig, ["image-sources-dir"])
        if "explicit-image-sources" in repoConfig:
            for explicitImageSource in repoConfig["explicit-image-sources"]:
                verifyPaths(
                    explicitImageSource,
                    ["image-file", "permissions-file", "build-context"])
        return repoConfig

    @property
    def imageSourcesDir(self):
        """
    Get the path of the repo's subuser root on disk on the host.
    """
        return os.path.join(self.repoPath, self.relativeImageSourcesDir)

    @property
    def relativeImageSourcesDir(self):
        """
    Get the path of the repo's subuser root on disk on the host.
    """
        repoConfig = self.repoConfig
        if repoConfig and "image-sources-dir" in repoConfig:
            return repoConfig["image-sources-dir"]
        else:
            return "./"

    def isInUse(self):
        """
    Are there any installed images or subusers from this repository?
    """
        for _, installedImage in self.user.installedImages.items():
            if self.name == installedImage.sourceRepoId:
                return True
            for _, subuser in self.user.registry.subusers.items():
                try:
                    if self.name == subuser.imageSource.repo.name:
                        return True
                except subuserlib.classes.subuser.NoImageSourceException:
                    pass
        return False

    @property
    def isLocal(self):
        if self.sourceDir:
            return True
        return False

    def removeGitRepo(self):
        """
    Remove the downloaded git repo associated with this repository from disk.
    """
        if not self.isLocal:
            shutil.rmtree(self.repoPath)

    def isPresent(self):
        """
    Returns True if the repository's files are present on the system. (Cloned or local)
    """
        return os.path.exists(self.repoPath)

    def updateSources(self, initialUpdate=False):
        """
    Pull(or clone) the repo's ImageSources from git origin.
    """
        if self.isLocal:
            return
        if not self.isPresent():
            new = True
            self.user.registry.log("Cloning repository " + self.name +
                                   " from " + self.gitOriginURI)
            if self.gitRepository.clone(self.gitOriginURI) != 0:
                self.user.registry.log("Clone failed.")
                return
        else:
            new = False
        try:
            self.gitRepository.run(["fetch", "--all"])
        except Exception:  # For some reason, git outputs normal messages to stderr.
            pass
        if self.updateGitCommitHash():
            if not new:
                self.user.registry.logChange("Updated repository " +
                                             self.displayName)
            if not initialUpdate:
                self.loadImageSources()

    def serializeToDict(self):
        """
    Return a dictionary which describes the image sources available in this repository.
    """
        imageSourcesDict = {}
        for name, imageSource in self.items():
            imageSourcesDict[name] = imageSource.serializeToDict()
        return imageSourcesDict

    def loadImageSources(self):
        """
    Load ImageSources from disk into memory.
    """
        imageNames = self.fileStructure.lsFolders(self.relativeImageSourcesDir)
        for imageName in imageNames:
            imageSource = ImageSource(self.user, self, imageName)
            if self.fileStructure.exists(
                    imageSource.getRelativePermissionsFilePath()):
                self[imageName] = imageSource
        if self.repoConfig is not None and "explicit-image-sources" in self.repoConfig:
            for imageName, config in self.repoConfig[
                    "explicit-image-sources"].items():
                assert config is not None
                self[imageName] = ImageSource(self.user,
                                              self,
                                              imageName,
                                              explicitConfig=config)

    def updateGitCommitHash(self):
        """
    Update the internally stored git commit hash to the current git HEAD of the repository.
    Returns True if the repository has been updated.
    Otherwise false.
    """
        if self.isLocal:
            return True
        # Default
        master = "refs/remotes/origin/master"
        newCommitHash = self.gitRepository.getHashOfRef(master)
        # First we check for version constraints on the repository.
        if self.gitRepository.getFileStructureAtCommit(master).exists(
                "./.subuser.json"):
            configFileContents = self.gitRepository.getFileStructureAtCommit(
                master).read("./.subuser.json")
            configAtMaster = json.loads(configFileContents)
            if "subuser-version-constraints" in configAtMaster:
                versionConstraints = configAtMaster[
                    "subuser-version-constraints"]
                subuserVersion = subuserlib.version.getSubuserVersion(
                    self.user)
                for constraint in versionConstraints:
                    if not len(constraint) == 3:
                        raise SyntaxError(
                            "Error in .subuser.json file. Invalid subuser-version-constraints."
                            + str(versionConstraints))
                    op, version, commit = constraint
                    from operator import lt, le, eq, ge, gt
                    operators = {
                        "<": lt,
                        "<=": le,
                        "==": eq,
                        ">=": ge,
                        ">": gt
                    }
                    try:
                        matched = operators[op](subuserVersion, version)
                    except KeyError:
                        raise SyntaxError(
                            "Error in .subuser.json file. Invalid subuser-version-constraints.  \""
                            + op + "\" is not a valid operator.\n\n" +
                            str(versionConstraints))
                    if matched:
                        try:
                            newCommitHash = self.gitRepository.getHashOfRef(
                                "refs/remotes/origin/" + commit)
                        except OSError as e:
                            if len(commit) == 40:
                                newCommitHash = commit
                            else:
                                raise e
                        break
                else:
                    raise SyntaxError(
                        "Error reading .subuser.json file, no version constraints matched the current subuser version ("
                        + subuserVersion + ").\n\n" + str(versionConstraints))
        updated = not (newCommitHash == self.gitCommitHash)
        self.gitCommitHash = newCommitHash
        self.__fileStructure = None
        return updated