"""Docker clone helpers: retain existing security and networks, publish no new ports.""" import copy,http.client,json,socket,subprocess,pathlib class UnixHTTP(http.client.HTTPConnection): def __init__(self): super().__init__('localhost',timeout=40) def connect(self): self.sock=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM);self.sock.settimeout(self.timeout);self.sock.connect('/var/run/docker.sock') def api(method,path,body=None): c=UnixHTTP();raw=None if body is None else json.dumps(body).encode() c.request(method,path,body=raw,headers={'Content-Type':'application/json'});r=c.getresponse();data=r.read();c.close() if r.status>=300:raise RuntimeError(f'Docker {method} {path} failed: {r.status} '+data.decode()[:300]) return json.loads(data) if data else None def inspect(name):return api('GET','/containers/'+name+'/json') def clone(d,name): cfg=copy.deepcopy(d['Config']);cfg['Hostname']=name;cfg['Image']=d['Image'] cfg['Labels']={k:v for k,v in cfg.get('Labels',{}).items() if not k.startswith('com.docker.compose.')} cfg['Labels']['huobao.local.worker']=name cfg['HostConfig']=copy.deepcopy(d['HostConfig']) for k in ('ContainerIDFile','PidMode','IpcMode'): if k=='ContainerIDFile':cfg['HostConfig'][k]='' cfg['HostConfig']['AutoRemove']=False return cfg def set_env(cfg,key,value): cfg['Env']=[x for x in cfg.get('Env',[]) if not x.startswith(key+'=')]+[key+'='+value] cfg['Cmd']=[key+'='+value if x.startswith(key+'=') else x for x in cfg.get('Cmd',[])] def replace_bind(cfg,destination,source): binds=cfg['HostConfig'].get('Binds') or [] found=[x for x in binds if x.split(':')[1]==destination] assert len(found)==1,(destination,found) parts=found[0].split(':');parts[0]=str(source) cfg['HostConfig']['Binds']=[(':'.join(parts) if x==found[0] else x) for x in binds] def write_json(path,value): path=pathlib.Path(path);path.write_text(json.dumps(value,ensure_ascii=False));path.chmod(0o600)