fe2cf6b5308a9de4ce117313064c238e8cf40a9b
[portage.git] / bin / binhost-snapshot
1 #!/usr/bin/python
2 # Copyright 2010-2012 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 import io
6 import optparse
7 import os
8 import sys
9 import textwrap
10
11 try:
12         from urllib.parse import urlparse
13 except ImportError:
14         from urlparse import urlparse
15
16 from os import path as osp
17 pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")
18 sys.path.insert(0, pym_path)
19 import portage
20
21 def parse_args(argv):
22         prog_name = os.path.basename(argv[0])
23         usage = prog_name + ' [options] ' + \
24                 '<src_pkg_dir> <snapshot_dir> <snapshot_uri> <binhost_dir>'
25
26         prog_desc = "This program will copy src_pkg_dir to snapshot_dir " + \
27                 "and inside binhost_dir it will create a Packages index file " + \
28                 "which refers to snapshot_uri. This is intended to solve race " + \
29                 "conditions on binhosts as described at http://crosbug.com/3225."
30
31         usage += "\n\n"
32         for line in textwrap.wrap(prog_desc, 70):
33                 usage += line + "\n" 
34
35         usage += "\n"
36         usage += "Required Arguments:\n\n"
37         usage += "  src_pkg_dir  - the source $PKGDIR\n"
38         usage += "  snapshot_dir - destination snapshot " + \
39                 "directory (must not exist)\n"
40         usage += "  snapshot_uri - URI which refers to " + \
41                 "snapshot_dir from the\n" + \
42                 "                 client side\n"
43         usage += "  binhost_dir  - directory in which to " + \
44                 "write Packages index with\n" + \
45                 "                 snapshot_uri"
46
47         parser = optparse.OptionParser(usage=usage)
48         parser.add_option('--hardlinks', help='create hardlinks (y or n, default is y)',
49                 choices=('y', 'n'))
50         parser.set_defaults(hardlinks='y')
51         options, args = parser.parse_args(argv[1:])
52
53         if len(args) != 4:
54                 parser.error("Required 4 arguments, got %d" % (len(args),))
55
56         return parser, options, args
57
58 def main(argv):
59         parser, options, args = parse_args(argv)
60
61         src_pkg_dir, snapshot_dir, snapshot_uri, binhost_dir = args
62         src_pkgs_index = os.path.join(src_pkg_dir, 'Packages')
63
64         if not os.path.isdir(src_pkg_dir):
65                 parser.error("src_pkg_dir is not a directory: '%s'" % (src_pkg_dir,))
66
67         if not os.path.isfile(src_pkgs_index):
68                 parser.error("src_pkg_dir does not contain a " + \
69                         "'Packages' index: '%s'" % (src_pkg_dir,))
70
71         parse_result = urlparse(snapshot_uri)
72         if not (parse_result.scheme and parse_result.netloc and parse_result.path):
73                 parser.error("snapshot_uri is not a valid URI: '%s'" % (snapshot_uri,))
74
75         if os.path.isdir(snapshot_dir):
76                 parser.error("snapshot_dir already exists: '%s'" % snapshot_dir)
77
78         try:
79                 os.makedirs(os.path.dirname(snapshot_dir))
80         except OSError:
81                 pass
82         if not os.path.isdir(os.path.dirname(snapshot_dir)):
83                 parser.error("snapshot_dir parent could not be created: '%s'" % \
84                         os.path.dirname(snapshot_dir))
85
86         try:
87                 os.makedirs(binhost_dir)
88         except OSError:
89                 pass
90         if not os.path.isdir(binhost_dir):
91                 parser.error("binhost_dir could not be created: '%s'" % binhost_dir)
92
93         cp_opts = 'RP'
94         if options.hardlinks == 'n':
95                 cp_opts += 'p'
96         else:
97                 cp_opts += 'l'
98
99         cp_cmd = 'cp -%s %s %s' % (
100                 cp_opts,
101                 portage._shell_quote(src_pkg_dir),
102                 portage._shell_quote(snapshot_dir)
103         )
104
105         ret = os.system(cp_cmd)
106         if not (os.WIFEXITED(ret) and os.WEXITSTATUS(ret) == os.EX_OK):
107                 return 1
108
109         infile = io.open(portage._unicode_encode(src_pkgs_index,
110                 encoding=portage._encodings['fs'], errors='strict'),
111                 mode='r', encoding=portage._encodings['repo.content'],
112                 errors='strict')
113
114         outfile = portage.util.atomic_ofstream(
115                 os.path.join(binhost_dir, "Packages"),
116                 encoding=portage._encodings['repo.content'],
117                 errors='strict')
118
119         for line in infile:
120                 if line[:4] == 'URI:':
121                         # skip existing URI line
122                         pass
123                 else:
124                         if not line.strip():
125                                 # end of header
126                                 outfile.write("URI: %s\n\n" % snapshot_uri)
127                                 break
128                         outfile.write(line)
129
130         for line in infile:
131                 outfile.write(line)
132
133         infile.close()
134         outfile.close()
135
136         return os.EX_OK
137
138 if __name__ == "__main__":
139         sys.exit(main(sys.argv))