Skip to content
image.py 47.3 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 sys
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)
    def _load_modules(self, lib_list, get_str):
        '''
        Load python module embedded in image

        Return a dict of {module_name: module object}
        '''
        if not lib_list:
            return {}
        arrow(u"Load libs")
        old_level = arrowlevel(1)
        gl ={}
        # order matter!
        lib_list.sort()
        for filename in lib_list:
            arrow(os.path.basename(filename))
            name = os.path.basename(filename).split('-', 1)[1][:-3]
            if name in gl:
                error('Module %s already loaded' % name)
            # extract source code
            try:
                code = get_str(filename)
            except Exception as e:
                raise ISError(u"Extracting lib %s fail: %s" %
                                (filename, e))
            gl[name] = istools.string2module(name, code, filename)
            # avoid ImportError when exec 'import name'
            sys.modules[name] = gl[name]
        arrowlevel(level=old_level)
        return gl

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")
        lib_path = os.path.join(path, "lib")
        # create base directories
Seblu's avatar
Seblu committed
        arrow("Creating base directories")
            for d in (path, build_path, parser_path, setup_path, payload_path,
                      lib_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", "lib"):
            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
        '''
Loading full blame...