Skip to content
image.py 45.6 KiB
Newer Older
# -*- python -*-
# -*- coding: utf-8 -*-
# This file is part of Installsystems.
# Installsystems is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# Installsystems is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with Installsystems.  If not, see <http://www.gnu.org/licenses/>.

import ConfigParser
import cStringIO
import difflib
import json
import re
import shutil
import stat
import subprocess
import tarfile
import time
import installsystems
import installsystems.template as istemplate
import installsystems.tools as istools
from installsystems.exception import *
from installsystems.printer import *
from installsystems.tools import PipeFile
from installsystems.tarball import Tarball
Seblu's avatar
Seblu committed

class Image(object):
Seblu's avatar
Seblu committed
    '''
    Abstract class of images
    '''
    # format should be a float  X.Y but for compatibility reason it's a string
    # before version 6, it's strict string comparaison
Seblu's avatar
Seblu committed
    extension = ".isimage"
    @staticmethod
    def check_image_name(buf):
Seblu's avatar
Seblu committed
        '''
        Check if @buf is a valid image name
Seblu's avatar
Seblu committed
        '''
        if re.match("^[-_\w]+$", buf) is None:
            raise ISError(u"Invalid image name %s" % buf)

    @staticmethod
    def check_image_version(buf):
Seblu's avatar
Seblu committed
        '''
        Check if @buf is a valid image version
Seblu's avatar
Seblu committed
        '''
        if re.match("^\d+$", buf) is None:
            raise ISError(u"Invalid image version %s" % buf)
    @staticmethod
    def compare_versions(v1, v2):
        '''
        For backward compatibility, image class offer a method to compare image versions
        But code is now inside tools
        return istools.compare_versions(v1, v2)
class SourceImage(Image):
Seblu's avatar
Seblu committed
    '''
    Image source manipulation class
    '''
    @classmethod
    def create(cls, path, force=False):
Seblu's avatar
Seblu committed
        '''
        Create an empty source image
        '''
        # check local repository
        if not istools.isfile(path):
            raise NotImplementedError("SourceImage must be local")
        # main path
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        build_path = os.path.join(path, "build")
        parser_path = os.path.join(path, "parser")
        setup_path = os.path.join(path, "setup")
Seblu's avatar
Seblu committed
        payload_path = os.path.join(path, "payload")
        # create base directories
Seblu's avatar
Seblu committed
        arrow("Creating base directories")
Sebastien Luttringer's avatar
Sebastien Luttringer committed
            for d in (path, build_path, parser_path, setup_path, payload_path):
                if not os.path.exists(d) or not os.path.isdir(d):
                    os.mkdir(d)
        except Exception as e:
            raise ISError(u"Unable to create directory: %s" % d, e)
        # create example files
Seblu's avatar
Seblu committed
        arrow("Creating examples")
        arrowlevel(1)
        # create dict of file to create
        examples = {}
        # create description example from template
        examples["description"] = {"path": "description",
                                   "content": istemplate.description % {
                "name": "",
                "version": "1",
                "description": "",
                "author": "",
                "is_min_version": installsystems.version}}
        # create changelog example from template
        examples["changelog"] = {"path": "changelog", "content": istemplate.changelog}
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        # create build example from template
        examples["build"] = {"path": "build/01-build.py", "content": istemplate.build}
        # create parser example from template
        examples["parser"] = {"path": "parser/01-parser.py", "content": istemplate.parser}
        # create setup example from template
        examples["setup"] = {"path": "setup/01-setup.py", "content": istemplate.setup}
        for name in examples:
            try:
                arrow(u"Creating %s example" % name)
                expath = os.path.join(path, examples[name]["path"])
                if not force and os.path.exists(expath):
                    warn(u"%s already exists. Skipping!" % expath)
                    continue
                open(expath, "w").write(examples[name]["content"])
            except Exception as e:
                raise ISError(u"Unable to create example file", e)
            # setting executable rights on files in setup and parser
Seblu's avatar
Seblu committed
            arrow("Setting executable rights on scripts")
            umask = os.umask(0)
            os.umask(umask)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
            for dpath in (build_path, parser_path, setup_path):
                for f in os.listdir(dpath):
Seblu's avatar
Seblu committed
                    istools.chrights(os.path.join(dpath, f), mode=0777 & ~umask)
        except Exception as e:
            raise ISError(u"Unable to set rights on %s" % pf, e)
Seblu's avatar
Seblu committed
        arrowlevel(-1)
Seblu's avatar
Seblu committed
    def __init__(self, path):
        # check local repository
        if not istools.isfile(path):
            raise NotImplementedError("SourceImage must be local")
Seblu's avatar
Seblu committed
        Image.__init__(self)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        self.base_path = os.path.abspath(path)
        for pathtype in ("build", "parser", "setup", "payload"):
            setattr(self, u"%s_path" % pathtype, os.path.join(self.base_path, pathtype))
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        self.check_source_image()
        self.description = self.parse_description()
        self.changelog = self.parse_changelog()
Seblu's avatar
Seblu committed
        # script tarball path
        self.image_name = u"%s-%s%s" % (self.description["name"],
                                        self.description["version"],
                                        self.extension)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
    def check_source_image(self):
Seblu's avatar
Seblu committed
        '''
        Check if we are a valid SourceImage directories
        '''
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        for d in (self.base_path, self.build_path, self.parser_path,
                  self.setup_path, self.payload_path):
            if not os.path.exists(d):
                raise ISError(u"Invalid source image: directory %s is missing" % d)
            if not os.path.isdir(d):
                raise ISError(u"Invalid source image: %s is not a directory" % d)
            if not os.access(d, os.R_OK|os.X_OK):
                raise ISError(u"Invalid source image: unable to access to %s" % d)
Seblu's avatar
Seblu committed
        if not os.path.exists(os.path.join(self.base_path, "description")):
            raise ISError("Invalid source image: no description file")
Sebastien Luttringer's avatar
Sebastien Luttringer committed
    def build(self, force=False, force_payload=False, check=True, script=True):
Seblu's avatar
Seblu committed
        '''
        Create packaged image
        '''
        # check if free to create script tarball
        if os.path.exists(self.image_name) and force == False:
            raise ISError("Tarball already exists. Remove it before")
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        # check python scripts
Sebastien Luttringer's avatar
Sebastien Luttringer committed
            self.check_scripts(self.build_path)
Seblu's avatar
Seblu committed
            self.check_scripts(self.parser_path)
            self.check_scripts(self.setup_path)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        # remove list
        rl = set()
        # run build script
        if script:
            rl |= set(self.run_scripts(self.build_path, self.payload_path))
        if force_payload:
            rl |= set(self.select_payloads())
        # remove payloads
Loading full blame...