#!/usr/bin/python
#  Licensed under the Apache License, Version 2.0 (the "License"); you may
#  not use this file except in compliance with the License. You may obtain
#  a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#  License for the specific language governing permissions and limitations
#  under the License.

# tardiff.py -- compare the tar package with git archive. Error out if
# it's different.  The files to exclude are stored in a file, one per line,
# and it's passed as argument to this script.
#
# You should run this script from the project directory. For example, if
# you are verifying the package for glance project, you should run this
# script from that directory.

import getopt
import sys
import os
import commands


class OpenStackTarDiff:
    """ main class to verify tar generated in each openstack projects """

    def __init__(self):
        self.init_vars()
        self.validate_args()
        self.check_env()

    def check_env(self):
        """ exit if dist/ directory already exists """
        if not self.package and os.path.exists(self.dist_dir):
            self.error(
                "dist directory '%s' exist. Please remove it before "
                "running this script" % self.dist_dir)

    def validate_args(self):
        try:
            opts = getopt.getopt(sys.argv[1:], 'hvp:e:',
                                 ['help', 'verbose', 'package=',
                                  'exclude='])[0]
        except getopt.GetoptError:
            self.usage('invalid option selected')

        for opt, value in opts:
            if (opt in ('-h', '--help')):
                self.usage()
            elif (opt in ('-e', '--exclude')):
                self.e_file = value
            elif (opt in ('-p', '--package')):
                self.package = value
            elif (opt in ('-v', '--verbose')):
                self.verbose = True
            else:
                self.usage('unknown option : ' + opt)
        if not self.e_file:
            self.usage('specify file name containing list of files to '
                       'exclude in tar diff')
        if not os.path.exists(self.e_file):
            self.usage("file '%s' does not exist" % self.e_file)
        if self.package and not os.path.exists(self.package):
            self.usage("package '%s' specified, but does not "
                       "exist" % self.package)

    def init_vars(self):
        self.dist_dir = 'dist/'
        self.verbose = False

        self.e_file = None
        self.project_name = None
        self.prefix = None
        self.package = None
        self.sdist_files = []
        self.exclude_files = []
        self.git_files = []
        self.missing_files = []

    def verify(self):
        self.get_exclude_files()
        self.get_project_name()
        self.get_sdist_files()
        self.prefix = self.sdist_files[0]
        self.get_git_files()

        for file in self.git_files:
            if os.path.basename(file) in self.exclude_files:
                self.debug("excluding file '%s'" % file)
                continue

            if file not in self.sdist_files:
                self.missing_files.append(file)
            else:
                #self.debug("file %s matches" % file)
                pass
        if len(self.missing_files) > 0:
            self.error("files missing in package: %s" % self.missing_files)
        print "SUCCESS: Generated package '%s' is valid" % self.package

    def get_project_name(self):
        """ get git project name """
        self.project_name = os.path.basename(os.path.abspath(os.curdir))

    def get_exclude_files(self):
        """ read the file and get file list """
        fh = open(self.e_file, 'r')
        content = fh.readlines()
        fh.close()
        self.debug("files to exclude: %s" % content)

        # remove trailing new lines.
        self.exclude_files = [x.strip() for x in content]

    def get_git_files(self):
        """ read file list from git archive """
        git_tar = os.path.join(os.getcwd(), '%s.tar' % self.project_name)
        try:
            a_cmd = ("git archive -o %s HEAD --prefix=%s" %
                     (git_tar, self.prefix))
            self.debug("executing command '%s'" % a_cmd)
            (status, out) = commands.getstatusoutput(a_cmd)
            if status != 0:
                self.debug("command '%s' returned status '%s'" %
                           (a_cmd, status))
                if os.path.exists(git_tar):
                    os.unlink(git_tar)
                self.error('git archive failed: %s' % out)
        except Exception as err:
            if os.path.exists(git_tar):
                os.unlink(git_tar)
            self.error('git archive failed: %s' % err)

        try:
            tar_cmd = "tar tf %s" % git_tar
            self.debug("executing command '%s'" % tar_cmd)
            (status, out) = commands.getstatusoutput(tar_cmd)
            if status != 0:
                self.error('invalid tar file: %s' % git_tar)
            self.git_files = out.split('\n')
            self.debug("Removing git archive ... %s ..." % git_tar)
            os.remove(git_tar)
        except Exception as err:
            self.error('unable to read tar: %s' % err)

    def get_sdist_files(self):
        """ create package for project and get file list in it"""
        if not self.package:
            try:
                sdist_cmd = "python setup.py sdist"
                self.debug("executing command '%s'" % sdist_cmd)
                (status, out) = commands.getstatusoutput(sdist_cmd)
                if status != 0:
                    self.error("command '%s' failed" % sdist_cmd)
            except Exception as err:
                self.error("command '%s' failed" % (sdist_cmd, err))

            self.package = os.listdir(self.dist_dir)[0]
            self.package = os.path.join(self.dist_dir, self.package)
        tar_cmd = "tar tzf %s" % self.package
        try:
            self.debug("executing command '%s'" % tar_cmd)
            (status, out) = commands.getstatusoutput(tar_cmd)
            if status != 0:
                self.error("command '%s' failed" % tar_cmd)
            #self.debug(out)
            self.sdist_files = out.split('\n')
        except Exception as err:
            self.error("command '%s' failed: %s" % (tar_cmd, err))

    def debug(self, msg):
        if self.verbose:
            sys.stdout.write('DEBUG: %s\n' % msg)

    def error(self, msg):
        sys.stderr.write('ERROR: %s\n' % msg)
        sys.exit(1)

    def usage(self, msg=None):
        if msg:
            stream = sys.stderr
        else:
            stream = sys.stdout
        stream.write("usage: %s [--help|h] [-v] "
                     "[-p|--package=sdist_package.tar.gz] "
                     "-e|--exclude=filename\n" % os.path.basename(sys.argv[0]))
        if msg:
            stream.write("\nERROR: " + msg + "\n")
            exitCode = 1
        else:
            exitCode = 0
        sys.exit(exitCode)

if __name__ == '__main__':
    tardiff = OpenStackTarDiff()
    tardiff.verify()